【quantity】3 Unit 物理量计算库(quantity.rs)
一、源码
下面代码是一个使用Rust实现的类型安全物理量计算库,支持单位自动推导和SI前缀转换。
//! 物理量计算库
//!
//! 提供类型安全的物理量计算功能,支持单位自动推导和SI前缀转换use typenum::{Integer, Sum, Diff, Z0, // 0P1, P2, P3, P6, P9, P12, P15, P18, P21, P24, P27, P30, // 正指数N1, N2, N3, N6, N9, N12, N15, N18, N21, N24, N27, N30 // 负指数
};
use super::unit::Unit;
use std::marker::PhantomData;
use std::ops::{Add, Sub, Mul, Div};
use std::ops::Deref;/// 物理量基础结构(无类型约束)
///
/// # 类型参数
/// - `T`: 存储的数值类型
/// - `Prefix`: SI前缀类型(typenum中的整数类型)
/// - `U`: 单位类型
#[derive(Debug, Clone, Copy)]
pub struct Quantity<T, Prefix, U>
wherePrefix: Integer,U: ?Sized,
{value: T,_marker: PhantomData<(Prefix, U)>, // 合并Prefix和U的类型标记
}impl<T, Prefix, U> Quantity<T, Prefix, U>
wherePrefix: Integer,U: ?Sized,
{/// 获取值引用pub fn get(&self) -> &T {&self.value}/// 设置新值pub fn set(&mut self, value: T) {self.value = value;}
}impl<T, Prefix, M, KG, S, A, K, MOL, CD> Quantity<T, Prefix, Unit<M, KG, S, A, K, MOL, CD>>
wherePrefix: Integer,M: Integer,KG: Integer,S: Integer,A: Integer,K: Integer,MOL: Integer,CD: Integer,
{/// 创建新物理量/// /// # 参数/// - `value`: 物理量的数值/// /// # 示例/// ```/// use quantity::quantity::Length;/// let length = Length::<f64>::new(5.0);/// ```pub fn new(value: T) -> Self {Self {value,_marker: PhantomData,}}/// 提取内部值pub fn into_inner(self) -> T {self.value}/// 转换前缀(不改变实际值)/// /// # 类型参数/// - `NewPrefix`: 新的SI前缀类型pub fn with_prefix<NewPrefix>(self) -> Quantity<T, NewPrefix, Unit<M, KG, S, A, K, MOL, CD>>whereNewPrefix: Integer,{Quantity::new(self.value)}/// 转换为无前缀形式pub fn without_prefix(self) -> Quantity<T, Z0, Unit<M, KG, S, A, K, MOL, CD>> {self.with_prefix()}/// 转换为指定前缀(自动调整值)/// /// # 类型参数/// - `NewPrefix`: 目标SI前缀类型/// /// # 约束/// - `T`: 必须支持从f64转换和乘法运算/// - `Prefix`和`NewPrefix`: 必须能计算差值/// /// # 示例/// ```/// use quantity::quantity::Length;/// use quantity::quantity::Milli;/// use quantity::quantity::Kilo;/// let length_m = Length::<f64, Kilo>::new(1.0); // 1 km/// let length_mm = length_m.convert_to::<Milli>(); // 1000 mm/// ```pub fn convert_to<NewPrefix>(self) -> Quantity<T, NewPrefix, Unit<M, KG, S, A, K, MOL, CD>>whereT: Mul<Output = T> + From<f64>,Prefix: Integer + Sub<NewPrefix>,NewPrefix: Integer,Diff<Prefix, NewPrefix>: Integer,{let exponent: i32 = Prefix::to_i32() - NewPrefix::to_i32();let factor = 10.0_f64.powi(exponent);Quantity::new(self.value * T::from(factor))}
}// ================ 运算实现 ================impl<T, Prefix: Integer, U> Deref for Quantity<T, Prefix, U> {type Target = T;/// 解引用获取内部值fn deref(&self) -> &T { &self.value }
}impl<T1, T2, Prefix, U> Add<Quantity<T2, Prefix, U>> for Quantity<T1, Prefix, U>
whereT1: Add<T2>, // 支持 T1 + T2 运算Prefix: Integer, // 前缀必须是整数类型U: Clone, // 单位类型需要可克隆<T1 as Add<T2>>::Output: Clone, // 加法结果类型需要可克隆
{type Output = Quantity<<T1 as Add<T2>>::Output, Prefix, U>;/// 物理量加法/// /// # 注意/// 要求两个物理量有相同的单位和前缀fn add(self, rhs: Quantity<T2, Prefix, U>) -> Self::Output {Quantity {value: self.value + rhs.value,_marker: PhantomData,}}
}impl<T1, T2, Prefix, U> Sub<Quantity<T2, Prefix, U>> for Quantity<T1, Prefix, U>
whereT1: Sub<T2>, // 支持 T1 - T2 运算Prefix: Integer, // 前缀必须是整数类型U: Clone, // 单位类型需要可克隆<T1 as Sub<T2>>::Output: Clone, // 减法结果类型需要可克隆
{type Output = Quantity<<T1 as Sub<T2>>::Output, Prefix, U>;/// 物理量减法/// /// # 注意/// 要求两个物理量有相同的单位和前缀fn sub(self, rhs: Quantity<T2, Prefix, U>) -> Self::Output {Quantity {value: self.value - rhs.value,_marker: PhantomData,}}
}impl<T1, T2, Prefix1, Prefix2, U1, U2> Mul<Quantity<T2, Prefix2, U2>> for Quantity<T1, Prefix1, U1>
whereT1: Mul<T2>, // 值类型可相乘Prefix1: Integer + Add<Prefix2>,Prefix2: Integer,U1: Mul<U2>, // 使用标准乘法 traitSum<Prefix1, Prefix2>: Integer, <T1 as Mul<T2>>::Output: Clone,<U1 as Mul<U2>>::Output: Clone,
{type Output = Quantity<<T1 as Mul<T2>>::Output,Sum<Prefix1, Prefix2>, <U1 as Mul<U2>>::Output // 单位相乘>;/// 物理量乘法/// /// # 注意/// 自动处理单位和前缀的乘法关系fn mul(self, rhs: Quantity<T2, Prefix2, U2>) -> Self::Output {Quantity {value: self.value * rhs.value,_marker: PhantomData,}}
}impl<T1, T2, Prefix1, Prefix2, U1, U2> Div<Quantity<T2, Prefix2, U2>> for Quantity<T1, Prefix1, U1>
whereT1: Div<T2>,Prefix1: Integer + Sub<Prefix2>, Prefix2: Integer,U1: Div<U2>,Diff<Prefix1, Prefix2>: Integer,<T1 as Div<T2>>::Output: Clone,<U1 as Div<U2>>::Output: Clone,
{type Output = Quantity<<T1 as Div<T2>>::Output,Diff<Prefix1, Prefix2>, // 前缀指数相减<U1 as Div<U2>>::Output>;/// 物理量除法/// /// # 注意/// 自动处理单位和前缀的除法关系fn div(self, rhs: Quantity<T2, Prefix2, U2>) -> Self::Output {Quantity {value: self.value / rhs.value,_marker: PhantomData,}}
}// ================ 类型别名 ================// 基本单位
/// 长度 (米)
pub type Length<T, P = NoPrefix> = Quantity<T, P, Unit<P1, Z0, Z0, Z0, Z0, Z0, Z0>>;
/// 质量 (千克)
pub type Mass<T, P = NoPrefix> = Quantity<T, P, Unit<Z0, P1, Z0, Z0, Z0, Z0, Z0>>;
/// 时间 (秒)
pub type Time<T, P = NoPrefix> = Quantity<T, P, Unit<Z0, Z0, P1, Z0, Z0, Z0, Z0>>;
/// 电流 (安培)
pub type Current<T, P = NoPrefix> = Quantity<T, P, Unit<Z0, Z0, Z0, P1, Z0, Z0, Z0>>;
/// 温度 (开尔文)
pub type Temperature<T, P = NoPrefix> = Quantity<T, P, Unit<Z0, Z0, Z0, Z0, P1, Z0, Z0>>;
/// 物质的量 (摩尔)
pub type Amount<T, P = NoPrefix> = Quantity<T, P, Unit<Z0, Z0, Z0, Z0, Z0, P1, Z0>>;
/// 发光强度 (坎德拉)
pub type LuminousIntensity<T, P = NoPrefix> = Quantity<T, P, Unit<Z0, Z0, Z0, Z0, Z0, Z0, P1>>;// 导出单位
/// 速度 (米/秒)
pub type Velocity<T, P = NoPrefix> = Quantity<T, P, Unit<P1, Z0, N1, Z0, Z0, Z0, Z0>>;
/// 加速度 (米/秒²)
pub type Acceleration<T, P = NoPrefix> = Quantity<T, P, Unit<P1, Z0, N2, Z0, Z0, Z0, Z0>>;
/// 力 (牛顿 = 千克·米/秒²)
pub type Force<T, P = NoPrefix> = Quantity<T, P, Unit<P1, P1, N2, Z0, Z0, Z0, Z0>>;
/// 能量 (焦耳 = 千克·米²/秒²)
pub type Energy<T, P = NoPrefix> = Quantity<T, P, Unit<P2, P1, N2, Z0, Z0, Z0, Z0>>;
/// 功率 (瓦特 = 千克·米²/秒³)
pub type Power<T, P = NoPrefix> = Quantity<T, P, Unit<P2, P1, N3, Z0, Z0, Z0, Z0>>;
/// 电压 (伏特 = 千克·米²/秒³·安培)
pub type Voltage<T, P = NoPrefix> = Quantity<T, P, Unit<P2, P1, N3, N1, Z0, Z0, Z0>>;// SI前缀
/// 无前缀 (10^0)
pub type NoPrefix = Z0;// 正前缀 (大数)
/// 昆它 (quetta) 10^30
pub type Quetta = P30;
/// 容那 (ronna) 10^27
pub type Ronna = P27;
/// 尧它 (yotta) 10^24
pub type Yotta = P24;
/// 泽它 (zetta) 10^21
pub type Zetta = P21;
/// 艾可萨 (exa) 10^18
pub type Exa = P18;
/// 拍它 (peta) 10^15
pub type Peta = P15;
/// 太拉 (tera) 10^12
pub type Tera = P12;
/// 吉咖 (giga) 10^9
pub type Giga = P9;
/// 兆 (mega) 10^6
pub type Mega = P6;
/// 千 (kilo) 10^3
pub type Kilo = P3;
/// 百 (hecto) 10^2
pub type Hecto = P2;
/// 十 (deca) 10^1
pub type Deca = P1;// 负前缀 (小数)
/// 分 (deci) 10^-1
pub type Deci = N1;
/// 厘 (centi) 10^-2
pub type Centi = N2;
/// 毫 (milli) 10^-3
pub type Milli = N3;
/// 微 (micro) 10^-6
pub type Micro = N6;
/// 纳诺 (nano) 10^-9
pub type Nano = N9;
/// 皮可 (pico) 10^-12
pub type Pico = N12;
/// 飞母托 (femto) 10^-15
pub type Femto = N15;
/// 阿托 (atto) 10^-18
pub type Atto = N18;
/// 仄普托 (zepto) 10^-21
pub type Zepto = N21;
/// 幺科托 (yocto) 10^-24
pub type Yocto = N24;
/// 柔托 (ronto) 10^-27
pub type Ronto = N27;
/// 亏科托 (quecto) 10^-30
pub type Quecto = N30;#[cfg(test)]
mod tests {use super::*;#[test]fn test_length_addition() {let l1 = Length::<f64>::new(5.0);let l2 = Length::<f64>::new(3.0);let sum = l1 + l2;assert_eq!(*sum, 8.0);}#[test]fn test_prefix_conversion() {let km = Length::<f64, Kilo>::new(1.0);let m = km.convert_to::<NoPrefix>();assert_eq!(*m, 1000.0);let mm = km.convert_to::<Milli>();assert_eq!(*mm, 1_000_000.0);}#[test]fn test_multiplication() {let length = Length::<f64>::new(2.0);let time = Time::<f64>::new(4.0);let velocity = length / time;assert_eq!(*velocity, 0.5);let acceleration = velocity / time;assert_eq!(*acceleration, 0.125);}#[test]fn test_force_calculation() {let mass = Mass::<f64>::new(10.0);let acceleration = Acceleration::<f64>::new(2.0);let force: Force<f64> = mass * acceleration;assert_eq!(*force, 20.0);}#[test]fn test_energy_calculation() {let force = Force::<f64>::new(5.0);let distance = Length::<f64>::new(3.0);let energy: Energy<f64> = force * distance;assert_eq!(*energy, 15.0);}
}
二、基础结构
Quantity 结构体
pub struct Quantity<T, Prefix, U>
wherePrefix: Integer,U: ?Sized,
{value: T,_marker: PhantomData<(Prefix, U)>,
}
-
T: 存储的数值类型(如f64, f32等)
-
Prefix: SI前缀类型(使用typenum中的整数类型表示,如Kilo, Milli等)
-
U: 单位类型(使用Unit结构表示,前文有相关叙述)
-
_marker: 使用PhantomData来在编译期跟踪类型参数而不需要运行时存储
核心方法
-
new(): 创建新物理量
-
get()/set(): 获取/设置值
-
into_inner(): 提取内部值
-
with_prefix(): 转换前缀(不改变实际值)
-
without_prefix(): 转换为无前缀形式
-
convert_to(): 转换为指定前缀(自动调整值)
三、单位系统
单位系统使用7个基本SI单位的幂次组合表示,详见前文有相关叙述。简单解释如下。
Unit<M, KG, S, A, K, MOL, CD>
每个类型参数表示对应基本单位的指数:
-
M: 米(长度)
-
KG: 千克(质量)
-
S: 秒(时间)
-
A: 安培(电流)
-
K: 开尔文(温度)
-
MOL: 摩尔(物质的量)
-
CD: 坎德拉(发光强度)
四、运算实现
库实现了四种基本运算:
加法 (Add)
impl<T1, T2, Prefix, U> Add<Quantity<T2, Prefix, U>> for Quantity<T1, Prefix, U>
-
要求两个量有相同单位和前缀
-
结果保持原单位和前缀
减法 (Sub)
impl<T1, T2, Prefix, U> Sub<Quantity<T2, Prefix, U>> for Quantity<T1, Prefix, U>
-
同样要求相同单位和前缀
-
结果保持原单位和前缀
乘法 (Mul)
impl<T1, T2, Prefix1, Prefix2, U1, U2> Mul<Quantity<T2, Prefix2, U2>> for Quantity<T1, Prefix1, U1>
-
单位和前缀会进行组合
-
新前缀 = Prefix1 + Prefix2
-
新单位 = U1 * U2(指数相加)
除法 (Div)
impl<T1, T2, Prefix1, Prefix2, U1, U2> Div<Quantity<T2, Prefix2, U2>> for Quantity<T1, Prefix1, U1>
-
单位和前缀会进行组合
-
新前缀 = Prefix1 - Prefix2
-
新单位 = U1 / U2(指数相减)
五、预定义类型
基本单位类型
pub type Length<T, P = NoPrefix> = Quantity<T, P, Unit<P1, Z0, Z0, Z0, Z0, Z0, Z0>>;
pub type Mass<T, P = NoPrefix> = Quantity<T, P, Unit<Z0, P1, Z0, Z0, Z0, Z0, Z0>>;
// 其他基本单位...
导出单位类型
pub type Velocity<T, P = NoPrefix> = Quantity<T, P, Unit<P1, Z0, N1, Z0, Z0, Z0, Z0>>;
pub type Force<T, P = NoPrefix> = Quantity<T, P, Unit<P1, P1, N2, Z0, Z0, Z0, Z0>>;
// 其他导出单位...
SI前缀类型
pub type NoPrefix = Z0; // 10^0
pub type Kilo = P3; // 10^3
pub type Milli = N3; // 10^-3
// 其他前缀...
六、测试用例
代码包含多个测试用例验证功能:
-
长度加法
-
前缀转换(如千米转米、毫米)
-
乘除法运算(如长度/时间=速度)
-
力和能量的计算
七、设计优势
-
类型安全:编译时检查单位一致性,防止单位不匹配的运算
-
零成本抽象:所有类型信息在编译期处理,运行时无额外开销
-
自动单位推导:乘除法自动推导出正确的单位和前缀
-
灵活的单位系统:可以表示任意SI导出单位
-
前缀支持:内置完整的SI前缀系统,支持自动转换
这个库非常适合需要精确物理量计算的科学和工程应用,能有效防止单位错误导致的bug。