一、源码
下面代码是一个使用Rust实现的类型安全物理量计算库,支持单位自动推导和SI前缀转换。
//! 物理量计算库
//!
//! 提供类型安全的物理量计算功能,支持单位自动推导和SI前缀转换
use typenum::{
Integer, Sum, Diff,
Z0, // 0
P1, 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>
where
Prefix: Integer,
U: ?Sized,
{
value: T,
_marker: PhantomData<(Prefix, U)>, // 合并Prefix和U的类型标记
}
impl<T, Prefix, U> Quantity<T, Prefix, U>
where
Prefix: 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>>
where
Prefix: 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>>
where
NewPrefix: 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>>
where
T: 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>
where
T1: 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>
where
T1: 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>
where
T1: Mul<T2>, // 值类型可相乘
Prefix1: Integer + Add<Prefix2>,
Prefix2: Integer,
U1: Mul<U2>, // 使用标准乘法 trait
Sum<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>
where
T1: 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>
where
Prefix: 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。