#si-units #units #unit

no-std simple-si-units

一个提供基本 SI 单位和常见转换的 Rust 库。SI 单位以模板类型的形式提供,以便您可以编写强制正确单位的 API

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科学 中排名

Download history 327/week @ 2024-03-11 16/week @ 2024-03-18 56/week @ 2024-04-01 1/week @ 2024-04-22 1/week @ 2024-05-27 8/week @ 2024-06-03 32/week @ 2024-06-10 16/week @ 2024-06-17 26/week @ 2024-06-24

82 每月下载量
用于 4 个 Crates(直接使用 2 个)

MPL-2.0 许可证

3.5MB
61K SLoC

simple-si-units

GitHub Workflow Build Status GitHub Workflow Test Status codecov Crate.io Redistribution license

此 Rust 库提供美国国家标准与技术研究院(NIST)指定的标准 SI 单位的编译器检查类型(本项目未获得 NIST 的官方认可)。

包含什么?

  • 官方标准 SI 单位
  • 常见次级单位,如速度
  • 实现运算符以自动在具有基本算术的单元之间进行转换(例如距离/时间=速度)
  • 单元是模板化的,因此您可以选择是否使用 f32f64 或其他类似数字的类型作为您的具体数字类型。
  • 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 - 如果启用,则单位结构体将实现 IntoFrom 特性,以在 simple-si-unitsuom 类型之间进行转换
  • num-bigfloat - 添加 core::ops::Mulcore::ops::Div 实现以乘以和除以 num-bigfloat 标量值
  • num-complex - 添加 core::ops::Mulcore::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>的数字类型,例如Complex64BigFloat(分别来自num-complexnum-bigfloat包)。

为什么不使用uom呢?

您不必选择,两者都可以使用!所有simple-si-units类型都实现了IntoFrom特质,以将它们转换为它们的uom等效类型。

uomsimple-si-units包都被设计用来提供科学单位的编译时类型检查,以帮助开发人员捕捉数学错误并编写更干净的计算API。区别在于uom还执行维度分析,但不能处理自定义数据类型,而simple-si-units处理任何类似数字的数据类型,但不会尝试实现完整的编译时维度分析。simple-si-units还优先考虑开发人员的易用性,遵循一致的Struct::from_...()Struct.to_...()语法,以实现简单直观的数字转换。您选择使用uomsimple-si-units取决于您的需求。

以下表格比较了simple-si-units v1.0uom v0.34,以帮助您决定使用哪一个

功能 simple-si-units uom
零成本的测量单位类型安全
所有由NIST定义的主要和次要SI单位
SI单位的倒数(即倒数) 部分
支持标准十进制类型(例如f64)
支持标准整数类型(例如i32) 部分** 部分**
支持num-bigfloat
支持num-complex
支持num-rational 部分**
支持用户定义和其他数字类型
编译时维度分析

** simple-si-units中不全面支持整数类型和基于int的数字类型

为了进一步展示simple-si-unitsuom之间的相似之处和不同之处,以下是同一重力计算函数的两个不同版本,一个使用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::Complexnum_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 crateNum 特性)。

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-bigfloatnum-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 + XX + &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