26 个版本 (4 个稳定版)
1.1.1 | 2023年5月4日 |
---|---|
0.9.1 | 2023年4月25日 |
0.7.4 | 2023年3月23日 |
0.3.1 | 2022年11月7日 |
29 在 科学 中排名
82 每月下载量
用于 4 个 Crates(直接使用 2 个)
3.5MB
61K SLoC
simple-si-units
此 Rust 库提供美国国家标准与技术研究院(NIST)指定的标准 SI 单位的编译器检查类型(本项目未获得 NIST 的官方认可)。
包含什么?
- 官方标准 SI 单位
- 常见次级单位,如速度
- 实现运算符以自动在具有基本算术的单元之间进行转换(例如距离/时间=速度)
- 单元是模板化的,因此您可以选择是否使用
f32
或f64
或其他类似数字的类型作为您的具体数字类型。 - 与 uom 兼容
单元
此 crate 提供以下单位的类型。以下未列出的其他类型的数量(例如 jolt)超出此 crate 的范围。
基本 SI 单位(和标准单位)
(使用 use simple_si_units::base::*;
导入)
- 距离,即长度(米)
- 质量(千克)
- 时间(秒)
- 温度(开尔文)
- 量,即数量(摩尔)
- 电流(安培)
- 亮度(坎德拉)
导出单位
化学 | 电磁 | 几何 | 机械 | 核 |
---|---|---|---|---|
催化活性(mol/s) | 电容(C/V,即 F) | 角度(弧度) | 加速度(m/s^2) | 吸收剂量(J/kg,即 Gy) |
浓度(mol/m^3,即 mM) | 电荷,即库仑(A.s,即 C) | 面积(m^2) | 角加速度(rad/s^2) | 剂量等效(J/kg,即 Sv) |
摩尔质量(kg/mol) | 电导率(1/欧姆,即 S) | 立体角(sr) | 角动量(kg.m^2.rad/s) | 放射性(1/s,即 Bq) |
摩尔浓度(mol/kg) | 照度(lm/m^2,即 lux) | 体积(m^3) | 角速度(rad/s) | |
比热容(J/kg.K) | 电感(Wb/A,即 H) | 面积密度(kg.m^2) | ||
光通量(cd.sr,即 lm) | 密度(kg/L) | |||
磁通量(V·s,又称Wb) | 能量(kg·m²/s²,又称J) | |||
磁通密度(Wb/m²,又称T) | 力(kg·m/s²,又称N) | |||
电阻(V/A,又称欧姆) | 频率(1/s,又称Hz) | |||
电压(W/A,又称V) | 转动惯量(kg·m²) | |||
动量(kg·m/s) | ||||
功率,又称瓦特(J/s,又称W) | ||||
压力(N/m²,又称Pa) | ||||
扭矩(kg·m²/s²,又称N·m) | ||||
速度(m/s) |
不包括什么?
- 不支持维度分析
- 不提供所有可能单位类型的详尽列表(但您可以使用此库来实现自己的)
- 不支持整数数字类型(请自行承担风险)
特性
simple-si-units 库具有以下可选功能,可以通过启用它们来提供额外的兼容性
- serde - 添加 serde 序列化/反序列化兼容性
- uom - 如果启用,则单位结构体将实现
Into
和From
特性,以在 simple-si-units 和 uom 类型之间进行转换 - num-bigfloat - 添加
core::ops::Mul
和core::ops::Div
实现以乘以和除以num-bigfloat
标量值 - num-complex - 添加
core::ops::Mul
和core::ops::Div
实现以乘以和除以num-complex
要在您的项目中启用这些功能,请将以下内容添加到您的 Cargo.toml
文件中的 [dependencies]
simple-si-units = { version = "1.1", features = ["serde", "uom", "num-bigfloat", "num-complex"] }
快速入门指南
基本用法
要使用 simple-si-units,只需将 simple-si-units = "1.1"
添加到您的 [dependencies]
部分,然后在您的 Cargo.toml
文件中导入您需要的单位,如下所示
use simple_si_units::base::*;
use simple_si_units::geometry::*;
use simple_si_units::mechanical::*;
fn main() {
let box_width = Distance::from_cm(33.5);
let box_length = Distance::from_cm(45.0);
let box_height = Distance::from_cm(23.5);
let carboard_density = AreaDensity::from_grams_per_square_meter(300.);
let box_volume = &box_width * &box_height * &box_length;
println!("Your box holds a total volume of {:.2} liters", box_volume.to_L());
let box_weight = (2. * &box_width * &box_length
+ 2. * &box_width * &box_height
+ 2. * &box_length * &box_height) * &carboard_density;
println!("Your box has a weight of {}", box_weight);
}
请注意,simple-si-units 结构体都实现了 core::ops::{Add,Sub,Mul,Div}
对值和引用的操作,这对于不实现 Copy
特性的数字类型很有用。
制作API
simple-si-units 是专门为帮助人们创建用于执行科学计算的库和函数的更安全的API而设计的。
对于大多数应用程序,您只需为每个变量指定SI单位类型和数据类型即可,如下所示
use simple_si_units::base::Distance;
use simple_si_units::geometry::Volume;
use std::f64::consts::PI;
pub fn sphere_volume(radius: Distance<f64>) -> Volume<f64> {
&radius * &radius * &radius * 4. / 3. * PI
}
但是,如果您想支持多个数据类型,那么您应该使用泛型模板函数。 simple-si-units 库提供 NumLike
类型,以帮助简化泛型API,例如
use simple_si_units::base::Distance;
use simple_si_units::geometry::Volume;
use std::f64::consts::PI;
use simple_si_units::NumLike;
pub fn sphere_volume<T>(radius: Distance<T>) -> Volume<T>
where T: NumLike + From<f64>
{
&radius * &radius * &radius * T::from(4. / 3. * PI)
}
上述通用函数适用于任何实现了From<f64>
的数字类型,例如Complex64
或BigFloat
(分别来自num-complex和num-bigfloat包)。
为什么不使用uom呢?
您不必选择,两者都可以使用!所有simple-si-units
类型都实现了Into
和From
特质,以将它们转换为它们的uom
等效类型。
uom
和simple-si-units
包都被设计用来提供科学单位的编译时类型检查,以帮助开发人员捕捉数学错误并编写更干净的计算API。区别在于uom
还执行维度分析,但不能处理自定义数据类型,而simple-si-units
处理任何类似数字的数据类型,但不会尝试实现完整的编译时维度分析。simple-si-units
还优先考虑开发人员的易用性,遵循一致的Struct::from_...()
和Struct.to_...()
语法,以实现简单直观的数字转换。您选择使用uom
或simple-si-units
取决于您的需求。
以下表格比较了simple-si-units v1.0
和uom v0.34
,以帮助您决定使用哪一个
功能 | simple-si-units | uom |
---|---|---|
零成本的测量单位类型安全 | ✅ | ✅ |
所有由NIST定义的主要和次要SI单位 | ✅ | ✅ |
SI单位的倒数(即倒数) | ✅ | 部分 |
支持标准十进制类型(例如f64) | ✅ | ✅ |
支持标准整数类型(例如i32) | 部分** | 部分** |
支持num-bigfloat | ✅ | ❌ |
支持num-complex | ✅ | ✅ |
支持num-rational | 部分** | ✅ |
支持用户定义和其他数字类型 | ✅ | ❌ |
编译时维度分析 | ❌ | ✅ |
** simple-si-units中不全面支持整数类型和基于int的数字类型
为了进一步展示simple-si-units
和uom
之间的相似之处和不同之处,以下是同一重力计算函数的两个不同版本,一个使用simple-si-units
,另一个使用uom
// simple-si-units version
mod simple_si_version {
use simple_si_units::base::{Distance, Mass};
use simple_si_units::mechanical::{Acceleration};
pub fn calc_gravity(mass: Mass<f64>, dist: Distance<f64>) -> Acceleration<f64> {
const G: f64 = 6.67408e-11; // m3 kg-1 s-2
let d_squared = dist * dist;
return Acceleration::from_mps2(G * mass.to_kg() / d_squared.to_m2())
}
fn test_gravity1() {
let radius = Distance::from_km(6378.1);
let mass = Mass::from_earth_mass(1.0);
println!("simple-si-units: Earth gravity at sea-level is {}", calc_gravity(mass, radius));
}
}
// uom version
mod uom_version {
use uom::si::f64::{Length, Mass, Acceleration};
use uom::si::length::*;
use uom::si::mass::*;
use uom::si::acceleration::*;
use uom::fmt::DisplayStyle::Abbreviation;
pub fn calc_gravity(mass: Mass, dist: Length) -> Acceleration {
const G: f64 = 6.67408e-11; // m3 kg-1 s-2
let d_squared = dist * dist;
return Acceleration::new::<meter_per_second_squared>(G * mass.value / d_squared.value)
}
fn test_gravity2() {
let radius = Length::new::<kilometer>(6378.1);
let mass = Mass::new::<kilogram>(5.972e24);
println!("uom: Earth gravity at sea-level is {}",
calc_gravity(mass, radius).into_format_args(meter_per_second_squared, Abbreviation));
}
}
工作原理
对于每种单位类型(例如距离),Simple SI Units提供了一个泛型结构体来表示该单位,并实现了常见的类型转换。例如,将距离除以时间会得到速度。
use simple_si_units::base::{Distance, Mass};
use simple_si_units::mechanical::{Acceleration};
pub fn calc_gravity(mass: Mass<f64>, dist: Distance<f64>) -> Acceleration<f64>{
const G: f64 = 6.67408e-11; // m3 kg-1 s-2
let d_squared = dist * dist;
return Acceleration::from_mps2(G * mass.to_kg() / d_squared.to_m2())
}
fn main(){
let a = calc_gravity(Mass::from_solar_mass(1.0), Distance::from_au(1.0));
println!("Solar gravity at Earth orbital distance: {}", a);
}
由于这些结构体使用内部数据类型的泛型模板,因此您可以使用任何类似数字的数据类型与这些结构体一起使用,包括num_complex::Complex和num_bigfloat::BigFloat(有关不实现From<f64>
的类型,请参见下面的限制部分)。
例如,可以将上述函数重写如下,以允许几乎任何类似数字的数据类型
use simple_si_units::base::{Distance, Mass};
use simple_si_units::mechanical::{Acceleration};
use simple_si_units::NumLike;
pub fn calc_gravity_generic<T>(mass: Mass<T>, dist: Distance<T>) -> Acceleration<T>
where T: NumLike+From<f64>
{
const G: f64 = 6.67408e-11; // m3 kg-1 s-2
let d_squared = &dist * &dist;
return Acceleration::from_mps2(T::from(G) * mass.to_kg() / d_squared.to_m2())
}
添加您自己的单位
简单的国际单位制(SI Units)并未提供可能的测量单位的全列表。要创建自己的单位,请使用 UnitStruct
程序宏和 NumLike
特性包(NumLike
只是 core::ops::*<Output=Self>+Clone+Debug+Display
的简写,如果你更喜欢,也可以使用来自 num-traits crate 的 Num
特性)。
use simple_si_units::{UnitStruct, NumLike};
#[derive(UnitStruct, Debug, Clone)]
struct HyperVelocity<T: NumLike>{
square_meters_per_second: T
}
fn weighted_hypervel_sum<T: NumLike>(a: HyperVelocity<T>, b: HyperVelocity<T>, weight: f64) -> HyperVelocity<T>
where T:NumLike + From<f64>
{
return weight*a + (1.-weight)*b;
}
请注意,UnitStruct
继承宏仅在包含单个成员变量的结构体上工作。否则,它将生成编译器错误。
局限性
由于 Rust 编译器在稳定 Rust 中缺乏 类型专用化,一些单位构造函数(例如 Mass::from_g(...)
)仅适用于实现了 From<f64>
的数字类型。这意味着这些函数将无法用于 Rust 的内置 f32
或整数类型。您仍然可以使用任何数字类型(例如 Mass::from_kg(1f32)
将正常工作)来构造单位结构体。
自定义数字类型
simple-si-units 可以与任何“类似数字”的数据类型一起工作,包括 num-bigfloat、num-complex 以及您自己定义的数字类型。如果数据类型实现了以下特性,则它就是“类似数字”的: Clone、Debug、Display、Add、AddAssign、Sub、SubAssign、Mul、MulAssign、Div、DivAssign、Neg
例如,下面是一个定义并使用类似于 f32
的数字类型的代码片段,它也实现了 From<f64>
use std::ops::*;
use std::fmt::{Display, Formatter, Result};
use simple_si_units::base::Mass;
use simple_si_units::geometry::Volume;
use simple_si_units::mechanical::Density;
#[derive(Debug, Copy, Clone)]
struct MyNumber(f32);
impl Display for MyNumber {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {std::fmt::Display::fmt(&self.0, f)}
}
impl Add for MyNumber {
type Output = MyNumber;
fn add(self, rhs: Self) -> Self::Output {MyNumber(self.0 + rhs.0)}
}
impl AddAssign for MyNumber {
fn add_assign(&mut self, rhs: Self) {self.0 += rhs.0;}
}
impl Sub for MyNumber {
type Output = MyNumber;
fn sub(self, rhs: Self) -> Self::Output {MyNumber(self.0 - rhs.0)}
}
impl SubAssign for MyNumber {
fn sub_assign(&mut self, rhs: Self) {self.0 -= rhs.0;}
}
impl Mul for MyNumber {
type Output = MyNumber;
fn mul(self, rhs: Self) -> Self::Output {MyNumber(self.0 * rhs.0)}
}
impl MulAssign for MyNumber {
fn mul_assign(&mut self, rhs: Self) {self.0 *= rhs.0;}
}
impl Div for MyNumber {
type Output = MyNumber;
fn div(self, rhs: Self) -> Self::Output {MyNumber(self.0 / rhs.0)}
}
impl DivAssign for MyNumber {
fn div_assign(&mut self, rhs: Self) {self.0 /= rhs.0;}
}
impl Neg for MyNumber {
type Output = MyNumber;
fn neg(self) -> Self::Output {MyNumber(-self.0)}
}
impl From<f64> for MyNumber{
fn from(value: f64) -> Self {MyNumber(value as f32)}
}
fn my_fn() -> Density<MyNumber>{
let m = Mass::from_g(MyNumber(1222.5_f32));
let v = Volume::from_L(MyNumber(11.3_f32));
return m / v;
}
强烈建议您还为您定义的所有值和引用类型的组合实现 std::ops::*
操作符(例如 X + X
、X + &X
、&X + X
和 &X + &X
),这将使您的数字类型更易于使用并与 simple-si-units 集成。
许可证
此库是开源的,受 Mozilla Public License version 2.0 许可。简而言之,您可以在开源和专有项目中包含此源代码 无需请求我的许可,但如果您修改了此库的源代码,则必须以开源许可证的形式提供您修改后的库版本。
开发人员说明
请注意,单元结构源文件(不包括 lib.rs
),全部由一个Python程序生成,该程序执行维度分析和其他代码生成活动,位于代码生成器文件夹中,该文件夹位于GitHub仓库。
如果您想贡献,请先添加您新特性的单元测试,然后修改Python项目以生成新特性的Rust实现。谢谢!
依赖关系
~1.1–2.9MB
~82K SLoC