项目结构
units/
├── Cargo.toml
├── src/
│ ├── lib.rs
│ ├── dimension.rs # 核心量纲系统
│ ├── ops.rs # 运算符重载
│ └── units/
│ ├── mod.rs
│ ├── base.rs # 基本单位定义(不含时间和长度)
│ ├── time.rs # 时间单位定义(从base.rs拆分)
│ ├── length.rs # 长度单位定义(从base.rs拆分)
│ ├── derived.rs # 导出单位定义
│ └── constants.rs # 物理常数
└── tests/
└── integration.rs # 集成测试
项目文件
- Cargo.toml
[package]
name = "unit"
version = "0.1.0"
edition = "2024"
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::Unit;
pub use units::{base, derived, constants};
/// 预导入常用单位
pub mod prelude {
pub use crate::dimension::Unit;
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};
/// SI单位系统实现(带词条头支持)
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Unit<
T,
const PREFIX: i32 = 0,
const METER: i32 = 0,
const KILOGRAM: i32 = 0,
const SECOND: i32 = 0,
const AMPERE: i32 = 0,
const KELVIN: i32 = 0,
const MOLE: i32 = 0,
const CANDELA: i32 = 0,
> {
pub value: T,
}
// 词条头常量定义
pub const KILO: i32 = 3;
pub const MILLI: i32 = -3;
/* 其他前缀... */
impl<T, const P: i32, const M: i32, const KG: i32>
Unit<T, P, M, KG>
{
pub fn new(value: T) -> Self { Self { value } }
pub fn cast<U: NumCast>(self) -> Option<Unit<U, P, M, KG>> {
NumCast::from(self.value).map(Unit::new)
}
}
impl<T: fmt::Display, const P: i32, const M: i32>
fmt::Display for Unit<T, P, M>
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if P != 0 { write!(f, "×10^{} ", P)?; }
/* 量纲显示逻辑 */
}
}
- src/ops.rs - 运算符重载
use std::ops::{Add, Sub, Mul, Div};
use num_traits::Num;
impl<T, const P: i32> Add for Unit<T, P> {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Unit::new(self.value + rhs.value)
}
}
macro_rules! impl_binop {
($op:ident, $method:ident) => {
impl<T, const P1: i32, const P2: i32>
$op<Unit<T, P2>> for Unit<T, P1>
{
type Output = Unit<T, {P1 + P2}>;
fn $method(self, rhs: Unit<T, P2>) -> Self::Output {
Unit::new(self.value.$method(rhs.value))
}
}
};
}
impl_binop!(Mul, mul);
impl_binop!(Div, div);
- src/units/ - 单位定义
- mod.rs
pub mod base;
pub mod length;
pub mod time;
pub mod derived;
pub mod constants;
pub use base::*;
pub use length::*;
pub use time::*;
pub use derived::*;
pub use constants::*;
- base.rs - 基本单位
use super::dimension::Unit;
/// 长度 - 米 (m)
pub type Meter<T> = Unit<T, 0, 1, 0, 0, 0, 0, 0, 0>;
/// 质量 - 千克 (kg)
pub type Kilogram<T> = Unit<T, 0, 0, 1, 0, 0, 0, 0, 0>;
/// 时间 - 秒 (s)
pub type Second<T> = Unit<T, 0,0, 0, 1, 0, 0, 0, 0>;
/// 电流 - 安培 (A)
pub type Ampere<T> = Unit<T,0, 0, 0, 0, 1, 0, 0, 0>;
/// 温度 - 开尔文 (K)
pub type Kelvin<T> = Unit<T, 0,0, 0, 0, 0, 1, 0, 0>;
/// 物质的量 - 摩尔 (mol)
pub type Mole<T> = Unit<T, 0, 0,0, 0, 0, 0, 1, 0>;
/// 发光强度 - 坎德拉 (cd)
pub type Candela<T> = Unit<T, 0,0, 0, 0, 0, 0, 0, 1>;
- length.rs - 长度单位
use super::dimension::Unit;
/// 长度 - 米 (m)
pub type Meter<T> = Unit<T, 0, 1, 0, 0, 0, 0, 0, 0>;
/// 千米 (km)
pub type Kilometer<T> = Unit<T, KILO, 1, 0, 0, 0, 0, 0, 0>;
/// 毫米 (mm)
pub type Millimeter<T> = Unit<T, MILLI, 1, 0, 0, 0, 0, 0, 0>;
- time.rs - 时间单位
use super::dimension::Unit;
/// 时间 - 秒 (s)
pub type Second<T> = Unit<T, 0, 0, 0, 1, 0, 0, 0, 0>;
/// 毫秒 (ms)
pub type Millisecond<T> = Unit<T, MILLI, 0, 0, 1, 0, 0, 0, 0>;
/// 小时 (h)
pub type Hour<T> = Unit<T, 0, 0, 0, 3600, 0, 0, 0, 0>;
- derived.rs - 导出单位
use super::{base::*, dimension::Unit};
/// 频率 - 赫兹 (Hz = s⁻¹)
pub type Hertz<T> = Unit<T, 0, 0, -1, 0, 0, 0, 0>;
/// 力 - 牛顿 (N = kg·m·s⁻²)
pub type Newton<T> = Unit<T, 1, 0, 1, -2, 0, 0, 0, 0>;
/// 能量 - 焦耳 (J = N·m = kg·m²·s⁻²)
pub type Joule<T> = Unit<T, 2, 1, -2, 0, 0, 0, 0>;
/// 功率 - 瓦特 (W = J/s = kg·m²·s⁻³)
pub type Watt<T> = Unit<T, 2, 1, -3, 0, 0, 0, 0>;
/// 电压 - 伏特 (V = W/A = kg·m²·s⁻³·A⁻¹)
pub type Volt<T> = Unit<T, 2, 1, -3, -1, 0, 0, 0>;
/// 电阻 - 欧姆 (Ω = V/A = kg·m²·s⁻³·A⁻²)
pub type Ohm<T> = Unit<T, 2, 1, -3, -2, 0, 0, 0>;
- constants.rs - 物理常数
use super::{base::*, dimension::Unit};
/// 光速 (m/s)
pub fn speed_of_light<T: crate::num_traits::Float>() -> Unit<T, 1, 0, -1, 0, 0, 0, 0> {
Unit::new(T::from(299792458).unwrap()
}
/// 普朗克常数 (J·s)
pub fn planck_constant<T: crate::num_traits::Float>() -> Unit<T, 2, 1, -1, 0, 0, 0, 0> {
Unit::new(T::from(6.62607015e-34).unwrap())
}
/// 阿伏伽德罗常数 (mol⁻¹)
pub fn avogadro_constant<T: crate::num_traits::Float>() -> Unit<T, 0, 0, 0, 0, 0, -1, 0> {
Unit::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);
}
设计说明文档
- 架构设计
类型系统架构:
Unit<T, PREFIX, 量纲...>
├─ 数值类型 T: 支持任意数值类型(f32/f64等)
├─ PREFIX: 词条头(10的幂次)
└─ 量纲参数: 7个基本量纲指数
运算规则:
-
加法/减法:要求完全相同的量纲和词条头
-
乘法/除法:量纲指数相加,词条头指数相加
-
单位转换:需显式调用转换方法
- 关键特性
编译期量纲检查:
let length = Meter::new(5.0);
let time = Second::new(2.0);
let _ = length + time; // 编译错误:量纲不匹配
词条头处理:
let km = Kilometer::new(2.0); // 2×10³ m
let mm = Meter::<f64, MILLI>::new(3000.0); // 3000×10⁻³ m
let sum = km + mm; // 需要统一单位
零成本抽象:
// 编译后等价于直接操作f64
let force: Newton<f64> = Kilogram::new(2.0) * Meter::new(3.0) / (Second::new(1.0).powi(2);
- 使用方法示例
基本运算:
let speed = Kilometer::new(120.0) / Hour::new(2.0);
println!("Speed: {} m/s", speed.to_meters_per_second());
物理公式:
fn kinetic_energy<T>(mass: Kilogram<T>, velocity: MeterPerSecond<T>) -> Joule<T>
where T: Float + Mul<Output = T>
{
T::from(0.5).unwrap() * mass * velocity.powi(2)
}
单位转换:
let temp = Celsius::new(25.0);
let kelvin: Kelvin<_> = temp.into();
- 测试案例
#[test]
fn test_energy_calculation() {
let mass = Kilogram::new(2.0);
let velocity = MeterPerSecond::new(3.0);
let energy = kinetic_energy(mass, velocity);
assert_eq!(energy.value, 9.0);
}
#[test]
fn test_prefix_conversion() {
let km = Kilometer::new(1.5);
let meters: Meter<_> = km.convert();
assert_eq!(meters.value, 1500.0);
}
- 扩展性设计
自定义单位:
pub type LightYear<T> = Unit<T, 0, 1, 0, 0, 0, 0, 0, 0>;
pub fn new_lightyear<T: Float>(value: T) -> LightYear<T> {
Unit::new(value * T::from(9.461e15).unwrap())
}
支持新数值类型:
impl<T: MyBigFloat, const P: i32> Unit<T, P> {
pub fn to_decimal_string(&self) -> String {
format!("{}e{}", self.value, P)
}
}
优势说明
-
类型安全:所有单位运算在编译期检查量纲一致性
-
高性能:无运行时开销,编译后与直接数值运算等效
-
表达力强:支持从阿托(10⁻¹⁸)到尧它(10²⁴)的词条头范围
-
可扩展:易于添加新的单位类型和特殊运算
-
符合SI标准:严格遵循国际单位制规范
该实现特别适合需要高精度单位管理的科学计算、工程仿真和金融计算等领域,在保证计算安全性的同时提供直观的单位表达方式。