【TeamFlow】4.3.1 SI单位系统库(Units)
项目结构
units/
├── Cargo.toml
├── src/
│ ├── lib.rs
│ ├── dimension.rs
│ ├── ops.rs
│ └── units/
│ ├── mod.rs
│ ├── base.rs
│ ├── derived.rs
│ └── constants.rs
└── tests/└── integration.rs
项目文件
- Cargo.toml
[package]
name = "si-units"
version = "0.1.0"
edition = "2021"
description = "Type-safe SI units implementation in Rust"
license = "MIT OR Apache-2.0"
repository = "https://github.com/example/si-units"[dependencies]
num-traits = "0.2" # 提供数值类型的通用操作
num = "0.4" # 额外的数值类型支持[dev-dependencies]
approx = "0.5" # 用于测试中的浮点比较
- src/lib.rs - 库根模块
//! 类型安全的SI单位系统实现
//!
//! 提供编译期量纲检查的单位运算能力#![warn(missing_docs)]
#![forbid(unsafe_code)]pub mod dimension;
pub mod ops;
pub mod units;pub use dimension::SIUnit;
pub use units::{base, derived, constants};/// 预导入常用单位
pub mod prelude {pub use crate::dimension::SIUnit;pub use crate::units::base::*;pub use crate::units::derived::*;pub use crate::units::constants::*;pub use crate::ops::*;
}
- src/dimension.rs - 核心量纲系统
use std::fmt;
use num_traits::{Num, NumCast, Float};/// SI单位系统的7个基本量纲
///
/// 使用const泛型参数表示各基本单位的指数:
/// - METER: 米 (长度)
/// - KILOGRAM: 千克 (质量)
/// - SECOND: 秒 (时间)
/// - AMPERE: 安培 (电流)
/// - KELVIN: 开尔文 (温度)
/// - MOLE: 摩尔 (物质的量)
/// - CANDELA: 坎德拉 (发光强度)
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SIUnit<T,const METER: i32,const KILOGRAM: i32,const SECOND: i32,const AMPERE: i32,const KELVIN: i32,const MOLE: i32,const CANDELA: i32,
> {/// 单位的数值部分pub value: T,
}impl<T, const M: i32, const KG: i32, const S: i32, const A: i32, const K: i32, const MOL: i32, const CD: i32>SIUnit<T, M, KG, S, A, K, MOL, CD>
{/// 创建新单位值pub fn new(value: T) -> Self {Self { value }}/// 转换数值类型pub fn cast<U: NumCast>(self) -> Option<SIUnit<U, M, KG, S, A, K, MOL, CD>> {NumCast::from(self.value).map(|v| SIUnit::new(v))}
}impl<T: fmt::Display, const M: i32, const KG: i32, const S: i32, const A: i32, const K: i32, const MOL: i32, const CD: i32>fmt::Display for SIUnit<T, M, KG, S, A, K, MOL, CD>
{fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {write!(f, "{} ", self.value)?;let dims = [("m", M), ("kg", KG), ("s", S),("A", A), ("K", K), ("mol", MOL), ("cd", CD)];for (symbol, exp) in dims {if *exp != 0 {write!(f, "·{}{}", symbol, superscript(*exp))?;}}Ok(())}
}/// 将数字转换为上标格式
fn superscript(n: i32) -> String {let mut s = String::new();for c in n.to_string().chars() {s.push(match c {'-' => '⁻','0' => '⁰','1' => '¹','2' => '²','3' => '³','4' => '⁴','5' => '⁵','6' => '⁶','7' => '⁷','8' => '⁸','9' => '⁹',_ => c,});}s
}
- src/ops.rs - 运算符重载
use std::ops::{Add, Sub, Mul, Div, Neg};
use crate::dimension::SIUnit;// 相同量纲的加减法
impl<T: Add<Output = U>, U, const M: i32, const KG: i32, const S: i32, const A: i32, const K: i32, const MOL: i32, const CD: i32>Add<SIUnit<T, M, KG, S, A, K, MOL, CD>> for SIUnit<T, M, KG, S, A, K, MOL, CD>
{type Output = SIUnit<U, M, KG, S, A, K, MOL, CD>;fn add(self, rhs: SIUnit<T, M, KG, S, A, K, MOL, CD>) -> Self::Output {SIUnit::new(self.value + rhs.value)}
}impl<T: Sub<Output = U>, U, const M: i32, const KG: i32, const S: i32, const A: i32, const K: i32, const MOL: i32, const CD: i32>Sub<SIUnit<T, M, KG, S, A, K, MOL, CD>> for SIUnit<T, M, KG, S, A, K, MOL, CD>
{type Output = SIUnit<U, M, KG, S, A, K, MOL, CD>;fn sub(self, rhs: SIUnit<T, M, KG, S, A, K, MOL, CD>) -> Self::Output {SIUnit::new(self.value - rhs.value)}
}// 乘除法 - 自动计算新量纲
macro_rules! impl_binop {($op:ident, $method:ident, ($($dim:ident),+) => {impl<T: $op<U, Output = V>, U, V, $(const $dim: i32),+, $(const RHS_$dim: i32),+>$op<SIUnit<U, $(RHS_$dim),+>> for SIUnit<T, $($dim),+>{type Output = SIUnit<V, $($dim + RHS_$dim),+>;fn $method(self, rhs: SIUnit<U, $(RHS_$dim),+>) -> Self::Output {SIUnit::new(self.value.$method(rhs.value))}}};
}impl_binop!(Mul, mul, METER, KILOGRAM, SECOND, AMPERE, KELVIN, MOLE, CANDELA);
impl_binop!(Div, div, METER, KILOGRAM, SECOND, AMPERE, KELVIN, MOLE, CANDELA);// 实现取反
impl<T: Neg<Output = U>, U, const M: i32, const KG: i32, const S: i32, const A: i32, const K: i32, const MOL: i32, const CD: i32>Neg for SIUnit<T, M, KG, S, A, K, MOL, CD>
{type Output = SIUnit<U, M, KG, S, A, K, MOL, CD>;fn neg(self) -> Self::Output {SIUnit::new(-self.value)}
}
- src/units/ - 单位定义
- base.rs - 基本单位
use super::dimension::SIUnit;/// 长度 - 米 (m)
pub type Meter<T> = SIUnit<T, 1, 0, 0, 0, 0, 0, 0>;/// 质量 - 千克 (kg)
pub type Kilogram<T> = SIUnit<T, 0, 1, 0, 0, 0, 0, 0>;/// 时间 - 秒 (s)
pub type Second<T> = SIUnit<T, 0, 0, 1, 0, 0, 0, 0>;/// 电流 - 安培 (A)
pub type Ampere<T> = SIUnit<T, 0, 0, 0, 1, 0, 0, 0>;/// 温度 - 开尔文 (K)
pub type Kelvin<T> = SIUnit<T, 0, 0, 0, 0, 1, 0, 0>;/// 物质的量 - 摩尔 (mol)
pub type Mole<T> = SIUnit<T, 0, 0, 0, 0, 0, 1, 0>;/// 发光强度 - 坎德拉 (cd)
pub type Candela<T> = SIUnit<T, 0, 0, 0, 0, 0, 0, 1>;
- derived.rs - 导出单位
use super::{base::*, dimension::SIUnit};/// 频率 - 赫兹 (Hz = s⁻¹)
pub type Hertz<T> = SIUnit<T, 0, 0, -1, 0, 0, 0, 0>;/// 力 - 牛顿 (N = kg·m·s⁻²)
pub type Newton<T> = SIUnit<T, 1, 1, -2, 0, 0, 0, 0>;/// 能量 - 焦耳 (J = N·m = kg·m²·s⁻²)
pub type Joule<T> = SIUnit<T, 2, 1, -2, 0, 0, 0, 0>;/// 功率 - 瓦特 (W = J/s = kg·m²·s⁻³)
pub type Watt<T> = SIUnit<T, 2, 1, -3, 0, 0, 0, 0>;/// 电压 - 伏特 (V = W/A = kg·m²·s⁻³·A⁻¹)
pub type Volt<T> = SIUnit<T, 2, 1, -3, -1, 0, 0, 0>;/// 电阻 - 欧姆 (Ω = V/A = kg·m²·s⁻³·A⁻²)
pub type Ohm<T> = SIUnit<T, 2, 1, -3, -2, 0, 0, 0>;
- constants.rs - 物理常数
use super::{base::*, dimension::SIUnit};/// 光速 (m/s)
pub fn speed_of_light<T: crate::num_traits::Float>() -> SIUnit<T, 1, 0, -1, 0, 0, 0, 0> {SIUnit::new(T::from(299792458).unwrap()
}/// 普朗克常数 (J·s)
pub fn planck_constant<T: crate::num_traits::Float>() -> SIUnit<T, 2, 1, -1, 0, 0, 0, 0> {SIUnit::new(T::from(6.62607015e-34).unwrap())
}/// 阿伏伽德罗常数 (mol⁻¹)
pub fn avogadro_constant<T: crate::num_traits::Float>() -> SIUnit<T, 0, 0, 0, 0, 0, -1, 0> {SIUnit::new(T::from(6.02214076e23).unwrap())
}
- tests/integration.rs - 集成测试
use si_units::prelude::*;
use approx::assert_relative_eq;#[test]
fn test_force_calculation() {let mass = Kilogram::new(5.0);let acceleration = Meter::new(2.0) / (Second::new(1.0) * Second::new(1.0));let force: Newton<f64> = mass * acceleration;assert_eq!(force.value, 10.0);
}#[test]
fn test_energy_calculation() {let force = Newton::new(10.0);let distance = Meter::new(3.0);let energy: Joule<f64> = force * distance;assert_eq!(energy.value, 30.0);
}#[test]
fn test_display_formatting() {let resistance = Ohm::new(4.7);assert_eq!(format!("{}", resistance), "4.7 ·kg¹·m²·s⁻³·A⁻²");
}#[test]
fn test_physical_constants() {let c: MeterPerSecond<f64> = speed_of_light();assert_relative_eq!(c.value, 299792458.0, epsilon = 1.0);let h = planck_constant::<f64>();assert_relative_eq!(h.value, 6.62607015e-34, max_relative = 1e-9);
}
设计亮点解析
- 类型安全的量纲系统
-
使用const泛型在编译期跟踪每个单位的量纲指数
-
非法运算(如 米 + 秒)会导致编译错误
- 零成本抽象
-
所有量纲检查在编译期完成
-
运行时等同于直接操作基础数值类型
- 灵活的数值支持
-
通过泛型支持任意数值类型(f32, f64, 自定义类型等)
-
兼容num-traits生态系统
- 清晰的物理关系表达
-
导出单位自动继承正确的量纲关系
-
如 牛顿 = 千克 × 米 / 秒² 直接体现在类型系统中
- 完备的显示支持
-
自动格式化为标准科学表示法
-
支持上标等专业数学符号
这个实现提供了强大的类型安全保证,同时保持了Rust的零成本抽象原则,非常适合科学计算、工程模拟等需要严格单位控制的场景。