当前位置: 首页 > news >正文

【uom】 2 quantity! 宏解析(quantity.rs)

源码

quantity! 宏是 uom (Units of Measurement) 库的核心组件,用于在 Rust 中实现类型安全的物理量计算和单位转换系统。该宏通过编译时类型检查确保单位计算的正确性,具有零运行时开销的特性。

/// Macro to implement a [quantity][quantity] and associated [measurement units][measurement]. Note
/// that this macro must be executed in direct submodules of the module where the [`system!`] macro
/// was executed. `@...` match arms are considered private.
///
/// * `$quantity_attr`: Quantity attributes. Generally used to set documentation comments for the
///   quantity.
/// * `$quantity`: Quantity name (e.g. `Length`).
/// * `$description`: Quantity description (e.g. `"length"`).
/// * `$dim_attr`: Dimension attributes. Generally used to set documentation comments for the
///   quantity's dimension type alias.
/// * `$system`: System of quantities type (e.g. `ISQ`).
/// * `$dimension`: Power of a factor for each base quantity in the system. Power should be
///   represented as a `typenum` type-level integer (e.g. `N1`, `Z0`, `P1`, `P2`, ...).
/// * `$kind`: [Kind][kind] of the quantity. Optional. This variable should only be specified when
///   defining a quantity that has the same dimensions as another quantity but isn't comparable.
///   When not specified [`crate::Kind`] is used.
/// * `$unit`: Unit name (e.g. `meter`, `foot`).
/// * `$conversion`: Conversion (coefficient and constant factor) from the unit to the base unit of
///   the quantity (e.g. `3.048_E-1` to convert `foot` to `meter`. `1.0_E0, 273.15_E0` to convert
///   `celsius` to `kelvin`.). The coefficient is required and the constant factor is optional.
///   Note that using a unit with a non-zero constant factor is not currently supported as a base
///   unit.
/// * `$abbreviation`: Unit abbreviation (e.g. `"m"`).
/// * `$singular`: Singular unit description (e.g. `"meter"`).
/// * `$plural`: Plural unit description (e.g. `"meters"`).
///
/// An example invocation is given below for the quantity of length in a meter-kilogram-second
/// system. The `#[macro_use]` attribute must be used when including the `uom` crate to make the
/// `quantity!` macro available.
///
/// ```
/// #[macro_use]
/// extern crate uom;
///
/// # fn main() { }
/// # mod mks {
/// #[macro_use]
/// mod length {
///     quantity! {
///         /// Length (base unit meter, m).
///         quantity: Length; "length";
///         /// Length dimension, m.
///         dimension: Q<P1 /*length*/, Z0 /*mass*/, Z0 /*time*/>;
///         units {
///             @meter: 1.0E0; "m", "meter", "meters";
///             @foot: 3.048E-1; "ft", "foot", "feet";
///         }
///     }
/// }
/// #     #[macro_use]
/// #     mod mass {
/// #         quantity! {
/// #             /// Mass (base unit kilogram, kg).
/// #             quantity: Mass; "mass";
/// #             /// Mass dimension, kg.
/// #             dimension: Q<Z0 /*length*/, P1 /*mass*/, Z0 /*time*/>;
/// #             units {
/// #                 @kilogram: 1.0; "kg", "kilogram", "kilograms";
/// #             }
/// #         }
/// #     }
/// #     #[macro_use]
/// #     mod time {
/// #         quantity! {
/// #             /// Time (base unit second, s).
/// #             quantity: Time; "time";
/// #             /// Time dimension, s.
/// #             dimension: Q<Z0 /*length*/, Z0 /*mass*/, P1 /*time*/>;
/// #             units {
/// #                 @second: 1.0; "s", "second", "seconds";
/// #             }
/// #         }
/// #     }
/// #     system! {
/// #         /// System of quantities, Q.
/// #         quantities: Q {
/// #             length: meter, L;
/// #             mass: kilogram, M;
/// #             time: second, T;
/// #         }
/// #         /// System of units, U.
/// #         units: U {
/// #             mod length::Length,
/// #             mod mass::Mass,
/// #             mod time::Time,
/// #         }
/// #     }
/// #     mod f32 {
/// #         Q!(crate::mks, f32/*, (centimeter, gram, second)*/);
/// #     }
/// # }
/// ```
///
/// [quantity]: https://jcgm.bipm.org/vim/en/1.1.html
/// [measurement]: https://jcgm.bipm.org/vim/en/1.9.html
/// [kind]: https://jcgm.bipm.org/vim/en/1.2.html
#[macro_export]
macro_rules! quantity {($(#[$quantity_attr:meta])* quantity: $quantity:ident; $description:expr;$(#[$dim_attr:meta])* dimension: $system:ident<$($dimension:ident),+>;$(kind: $kind:ty;)?units {$($(#[$unit_attr:meta])* @$unit:ident: $($conversion:expr),+; $abbreviation:expr,$singular:expr, $plural:expr;)+}) => {mod __system {pub use super::super::*;}$(#[$dim_attr])*pub type Dimension = __system::$system<$($crate::typenum::$dimension),+,quantity!(@kind $($kind)?)>;$(#[$quantity_attr])*////// ## Generic Parameters/// * `U`: Base units./// * `V`: Underlying storage type.pub type $quantity<U, V> = __system::Quantity<Dimension, U, V>;/// Marker trait to identify measurement units for the quantity. See/// [`Unit`](__system::Unit).pub trait Unit: __system::Unit {}/// Trait to identify [units][units] which have a [conversion factor][factor] for the/// `Quantity`. See [`crate::Conversion<V>`].////// ## Generic Parameters/// * `V`: Underlying storage type trait is implemented for.////// [units]: https://jcgm.bipm.org/vim/en/1.13.html/// [factor]: https://jcgm.bipm.org/vim/en/1.24.html#[allow(dead_code)]pub trait Conversion<V>: Unit + $crate::Conversion<V, T = <V as $crate::Conversion<V>>::T>whereV: $crate::Conversion<V>,{/// Check unit validity to ensure the unit is valid for the underlying storage type.#[cfg(test)]fn is_valid() -> bool;}unit! {@units $($(#[$unit_attr])* @$unit: $($conversion),+;$abbreviation, $singular, $plural;)+}/// Quantity description.#[must_use = "method returns a static value"]#[allow(dead_code)]#[inline(always)]pub const fn description() -> &'static str {$description}/// Unit enum.#[allow(non_camel_case_types)]#[non_exhaustive]#[allow(clippy::manual_non_exhaustive)]#[derive(Debug, Clone, Copy)]pub enum Units {$(#[allow(clippy::empty_docs)] // macros cannot expand to enum variants#[doc=$plural]$unit($unit),)+}impl Units {/// Unit abbreviation.#[must_use = "method returns a static value"]#[allow(dead_code)]pub fn abbreviation(&self) -> &'static str {match self {$(Units::$unit(_) => <$unit as __system::Unit>::abbreviation(),)+}}/// Unit singular description.#[must_use = "method returns a static value"]#[allow(dead_code)]pub fn singular(&self) -> &'static str {match self {$(Units::$unit(_) => <$unit as __system::Unit>::singular(),)+}}/// Unit plural description.#[must_use = "method returns a static value"]#[allow(dead_code)]pub fn plural(&self) -> &'static str {match self {$(Units::$unit(_) => <$unit as __system::Unit>::plural(),)+}}}static ALL_UNITS: &[Units] = &[$(Units::$unit($unit),)+];/// Iterate over all defined units for this quantity.#[allow(dead_code)]pub fn units() -> impl Iterator<Item = Units> {ALL_UNITS.iter().copied()}impl<U, V> $quantity<U, V>whereU: __system::Units<V> + ?Sized,V: $crate::num::Num + $crate::Conversion<V>,{/// Create a new quantity from the given value and measurement unit.////// ## Generic Parameters/// * `N`: Unit.#[must_use = "method produces a new value"]#[inline(always)]pub fn new<N>(v: V) -> SelfwhereN: Unit + $crate::Conversion<V, T = V::T>,{$quantity {dimension: $crate::lib::marker::PhantomData,units: $crate::lib::marker::PhantomData,value: __system::to_base::<Dimension, U, V, N>(&v),}}/// Retrieve the value of the quantity in the given measurement unit.////// ## Generic Parameters/// * `N`: Unit.#[must_use = "method returns a new number and does not mutate the original value"]#[inline(always)]pub fn get<N>(&self) -> VwhereN: Unit + $crate::Conversion<V, T = V::T>,{__system::from_base::<Dimension, U, V, N>(&self.value)}/// Returns the largest integer less than or equal to a number in the given/// measurement unit.////// ## Generic Parameters/// * `N`: Unit.#[must_use = "method returns a new number and does not mutate the original value"]#[inline(always)]pub fn floor<N>(self) -> SelfwhereV: $crate::num::Float,N: Unit + $crate::Conversion<V, T = V::T>,{Self::new::<N>(self.get::<N>().floor())}/// Returns the smallest integer less than or equal to a number in the given/// measurement unit.////// ## Generic Parameters/// * `N`: Unit.#[must_use = "method returns a new number and does not mutate the original value"]#[inline(always)]pub fn ceil<N>(self) -> SelfwhereV: $crate::num::Float,N: Unit + $crate::Conversion<V, T = V::T>,{Self::new::<N>(self.get::<N>().ceil())}/// Returns the nearest integer to a number in the in given measurement unit./// Round half-way cases away from 0.0.////// ## Generic Parameters/// * `N`: Unit.#[must_use = "method returns a new number and does not mutate the original value"]#[inline(always)]pub fn round<N>(self) -> SelfwhereV: $crate::num::Float,N: Unit + $crate::Conversion<V, T = V::T>,{Self::new::<N>(self.get::<N>().round())}/// Returns the integer part of a number in the given measurement unit.////// ## Generic Parameters/// * `N`: Unit.#[must_use = "method returns a new number and does not mutate the original value"]#[inline(always)]pub fn trunc<N>(self) -> SelfwhereV: $crate::num::Float,N: Unit + $crate::Conversion<V, T = V::T>,{Self::new::<N>(self.get::<N>().trunc())}/// Returns the fractional part of a number in the given measurement unit.////// ## Generic Parameters/// * `N`: Unit.#[must_use = "method returns a new number and does not mutate the original value"]#[inline(always)]pub fn fract<N>(self) -> SelfwhereV: $crate::num::Float,N: Unit + $crate::Conversion<V, T = V::T>,{Self::new::<N>(self.get::<N>().fract())}/// Creates a struct that can be used to format a compatible quantity for display.////// # Notes/// The return value of this method cannot be used to print directly, but is instead/// used to format quantities and can be reused; see/// [`Arguments::with`](super::fmt::Arguments::with()) and the examples below.////// If you do not need to format multiple quantities, consider using/// [`into_format_args`](#method.into_format_args) instead.////// # Examples#[cfg_attr(all(feature = "si", feature = "f32"), doc = " ```rust")]#[cfg_attr(not(all(feature = "si", feature = "f32")), doc = " ```rust,ignore")]/// # use uom::si::f32::*;/// # use uom::si::time::{femtosecond, picosecond};/// # use uom::si::fmt::Arguments;/// # use uom::fmt::DisplayStyle::*;/// let t1 = Time::new::<femtosecond>(1.0_E-1);/// let t2 = Time::new::<picosecond>(1.0_E-1);/// let a = Time::format_args(femtosecond, Description);////// assert_eq!("0.1 femtoseconds", format!("{}", a.with(t1)));/// assert_eq!("100 femtoseconds", format!("{}", a.with(t2)));/// ```////// ## Generic Parameters/// * `N`: Unit.#[must_use = "method returns a new object"]pub fn format_args<N>(_unit: N,style: $crate::fmt::DisplayStyle) -> __system::fmt::Arguments<Dimension, N>whereN: Unit{__system::fmt::Arguments {dimension: $crate::lib::marker::PhantomData,unit: $crate::lib::marker::PhantomData,style,}}/// Creates a struct that formats `self` for display.////// # Notes/// Unlike [`format_args`](#method.format_args), the return value of this method can be/// used directly for display. It will format the value of `self` for the quantity on/// which it is called and nothing else.////// If you wish to reuse the return value to format multiple quantities, use/// [`format_args`](#method.format_args) instead.////// # Examples#[cfg_attr(all(feature = "si", feature = "f32"), doc = " ```rust")]#[cfg_attr(not(all(feature = "si", feature = "f32")), doc = " ```rust,ignore")]/// # use uom::si::f32::*;/// # use uom::si::time::{femtosecond, picosecond};/// # use uom::si::fmt::Arguments;/// # use uom::fmt::DisplayStyle::*;/// let t = Time::new::<picosecond>(1.0_E-1);/// let a = t.into_format_args(femtosecond, Description);////// assert_eq!("100 femtoseconds", format!("{}", a));/// ```////// ## Generic Parameters/// * `N`: Unit.#[must_use = "method returns a new object and does not mutate the original one"]pub fn into_format_args<N>(self,_unit: N,style: $crate::fmt::DisplayStyle) -> __system::fmt::QuantityArguments<Dimension, U, V, N>whereN: Unit{__system::fmt::QuantityArguments {arguments: __system::fmt::Arguments {dimension: $crate::lib::marker::PhantomData,unit: $crate::lib::marker::PhantomData,style,},quantity: self,}}}impl<N> __system::fmt::Arguments<Dimension, N>whereN: __system::Unit + Unit,{/// Specifies a quantity to display.////// ## Generic Parameters/// * `U`: Base units./// * `V`: Underlying storage type trait is implemented for.#[must_use = "method returns a new object and does not mutate the original one"]pub fn with<U, V>(self,quantity: $quantity<U, V>) -> __system::fmt::QuantityArguments<Dimension, U, V, N>whereU: __system::Units<V> + ?Sized,V: $crate::num::Num + $crate::Conversion<V>,{__system::fmt::QuantityArguments {arguments: self,quantity,}}}mod str {storage_types! {use $crate::lib::str::FromStr;use $crate::str::ParseQuantityError::*;impl<U> FromStr for super::super::$quantity<U, V>whereU: super::super::__system::Units<V> + ?Sized,{type Err = $crate::str::ParseQuantityError;fn from_str(s: &str) -> Result<Self, Self::Err> {let mut parts = s.splitn(2, ' ');let value = parts.next().unwrap();let unit = parts.next().ok_or(NoSeparator)?;let value = value.parse::<V>().map_err(|_| ValueParseError)?;#[allow(unreachable_patterns)]match unit.trim() {$($abbreviation | $singular | $plural => Ok(Self::new::<super::super::$unit>(value)),)+_ => Err(UnknownUnit),}}}}}};(@kind $kind:ty) => { $kind };(@kind) => { dyn $crate::Kind };
}

二、宏参数详解与架构设计

1. 宏参数完整说明
/// Macro to implement a [quantity][quantity] and associated [measurement units][measurement]...
#[macro_export]
macro_rules! quantity {($(#[$quantity_attr:meta])* quantity: $quantity:ident; $description:expr;$(#[$dim_attr:meta])* dimension: $system:ident<$($dimension:ident),+>;$(kind: $kind:ty;)?units {$($(#[$unit_attr:meta])* @$unit:ident: $($conversion:expr),+; $abbreviation:expr, $singular:expr, $plural:expr;)+}) => { /* 宏展开内容 */ };(@kind $kind:ty) => { $kind };(@kind) => { dyn $crate::Kind };
}

参数说明补充:

  • $unit_attr: 单位元属性,用于单位级别的文档注释

  • 转换系数支持科学计数法表示(如3.048E-1)

  • 支持温度等需要常数因子的特殊转换(如1.0_E0, 273.15_E0)

2. 核心架构实现
维度系统增强实现
pub type Dimension = __system::$system<$($crate::typenum::$dimension),+,quantity!(@kind $($kind)?)>;

改进点:

  • 增加了对物理量种类(Kind)的支持

  • 使用typenum的类型级整数进行维度计算

  • 支持7个基本量的任意幂次组合(P1表示正幂,Z0表示零幂,N1表示负幂)

物理量类型扩展
pub type $quantity<U, V> = __system::Quantity<Dimension, U, V>;

泛型参数说明:

  • U: 单位类型,实现Units trait

  • V: 数值类型,需实现num::Num和Conversion trait

  • 使用PhantomData标记维度信息,零运行时开销

三、关键功能深度实现

1. 单位系统实现细节
unit! {@units $($(#[$unit_attr])* @$unit: $($conversion),+;$abbreviation, $singular, $plural;)+
}

内部生成:

  • 为每个单位生成独立的结构体

  • 实现Unit和Conversion trait

  • 包含转换系数和常数因子

  • 自动生成单位枚举Units和迭代器

2. 数学运算完整实现
pub fn floor<N>(self) -> Self
whereV: $crate::num::Float,N: Unit + $crate::Conversion<V, T = V::T>,
{Self::new::<N>(self.get::<N>().floor())
}

特点:

  • 保持单位一致性的舍入运算

  • 支持所有浮点数运算方法

  • 编译时单位类型检查

3. 格式化系统实现
pub fn into_format_args<N>(self,_unit: N,style: $crate::fmt::DisplayStyle
) -> __system::fmt::QuantityArguments<Dimension, U, V, N>
{/* 实现细节 */
}

格式化策略:

  • 支持缩写、单数和复数形式

  • 可复用的格式化器设计

  • 显示样式控制(Description, Abbreviation)

四、类型安全机制详解

1. 维度检查实现
impl<U, V> Add for $quantity<U, V>
whereU: Units<V>,V: Num + Conversion<V>,
{type Output = Self;fn add(self, rhs: Self) -> Self::Output {Self {dimension: PhantomData,units: PhantomData,value: self.value + rhs.value,}}
}

安全保证:

  • 编译时检查操作数维度匹配

  • 非法运算直接导致编译错误

  • 自动单位系统转换

2. 单位转换实现
pub fn new<N>(v: V) -> Self
whereN: Unit + $crate::Conversion<V, T = V::T>,
{$quantity {dimension: PhantomData,units: PhantomData,value: __system::to_base::<Dimension, U, V, N>(&v),}
}

转换过程:

  • 输入值按指定单位解释

  • 转换为基本单位存储

  • 转换系数编译期计算

  • 常数因子运行时处理

五、扩展性与最佳实践

1. 自定义单位实现示例
quantity! {/// 自定义压强单位quantity: MyPressure; "custom pressure";dimension: ISQ<N1, P1, N2>;  // L^-1 M^1 T^-2units {@my_unit: 133.322; "mu", "my unit", "my units";@standard_unit: 101325.0; "std", "standard", "standards";}
}
2. 错误处理最佳实践
impl<U> FromStr for $quantity<U, V> {type Err = ParseQuantityError;fn from_str(s: &str) -> Result<Self, Self::Err> {// 严格解析逻辑}
}

错误类型:

  • NoSeparator: 缺少值单位分隔符

  • ValueParseError: 数值解析失败

  • UnknownUnit: 未知单位名称

六、性能优化策略

  • 编译期计算:所有单位转换系数在编译期确定

  • 零成本抽象:运行时无动态分派或类型擦除

  • 内联优化:关键方法标记为#[inline(always)]

  • 格式化复用:重用格式化器减少开销

#[inline(always)]
pub fn get<N>(&self) -> V {__system::from_base::<Dimension, U, V, N>(&self.value)
}

七、完整应用案例

#[macro_use]
mod thermodynamics {quantity! {/// 温度(基本单位:开尔文)quantity: Temperature; "temperature";dimension: ISQ<Z0, Z0, P0, Z0, Z0, Z0, Z0>;  // 基本量均为零次幂units {@kelvin: 1.0; "K", "kelvin", "kelvins";@celsius: 1.0, 273.15; "°C", "degree Celsius", "degrees Celsius";@fahrenheit: 5.0/9.0, 459.67*(5.0/9.0); "°F", "degree Fahrenheit", "degrees Fahrenheit";}}
}// 使用示例
let boiling = Temperature::new::<celsius>(100.0);
let freezing = Temperature::new::<fahrenheit>(32.0);
let delta = boiling - freezing;  // 自动转换为开尔文计算

八、实现原理深度解析

1. 类型级编程体系
pub type Dimension = __system::$system<$($crate::typenum::$dimension),+,quantity!(@kind $($kind)?)
>;

关键技术:

  • 使用typenum进行维度计算

  • 类型级整数表示幂次

  • PhantomData标记单位/维度

2. trait系统设计
pub trait Conversion<V>: Unit + $crate::Conversion<V, T = <V as $crate::Conversion<V>>::T>
whereV: $crate::Conversion<V>,
{#[cfg(test)]fn is_valid() -> bool;
}

关键trait:

  • Unit: 标记有效单位

  • Conversion: 处理单位转换

  • Units: 单位系统约束

九、总结

quantity! 宏通过精妙的类型系统设计,在保持Rust零成本抽象优势的同时,提供了强大的单位安全保证。其主要特点包括:

  • 完全的类型安全:编译时检查所有单位运算

  • 零运行时开销:完全基于编译期计算

  • 灵活的扩展性:支持自定义单位和物理量

  • 丰富的功能:数学运算、格式化、解析等

  • 工程友好:完善的文档和错误处理

该宏特别适合科学计算、工程仿真、金融分析等需要严格单位控制的领域,是Rust生态中物理量处理的标杆实现。

相关文章:

  • 冠军之选,智启未来——解码威码逊的品牌传奇与冠军代言故事
  • Flowith:解放思维的AI画布让创意与效率如泉涌
  • 《解锁LibTorch:开启C++深度学习新征程》
  • 数学:拉马努金如何想出计算圆周率的公式?
  • 北极星 新美团核销接口对接
  • getattr 的作用
  • PCB设计实战技巧宝典:从库管理到布线优化的全流程解析
  • 多数元素题解(LC:169)
  • “BYD SHENZHEN”启运,搭载超7000台比亚迪新能源车前往巴西
  • QT6 源(62)篇五:阅读与注释 QString 这个类,先给出官方综述,带一些翻译。总篇目太大,代码就有 2000 行
  • 机器学习,深度学习
  • 全面接入!Qwen3现已上线千帆
  • 第八节:目录操作
  • 晶振:从消费电子到航天领域的时间精度定义者
  • 第六部分:实战项目与拓展
  • VS Code技巧2:识别FreeCAD对象
  • 可视化图解算法:判断是否完全二叉树
  • 基于SpringBoot的母婴商城系统设计与实现(附源码+PPT+论文)
  • 【操作系统】进程和线程的区别
  • /var/log/sssd/` 目录解析
  • 大学2025丨对话深大人工智能学院负责人李坚强:产学研生态比“造天才”更重要
  • 马克思主义理论研究教学名师系列访谈|杜玉华:马克思主义是“认识世界”和“改变世界”的思维工具
  • 马上评丨上热搜的协和“4+4”模式,如何面对舆论审视
  • A股三大股指小幅低收:电力股大幅调整,两市成交10221亿元
  • 看展览|建造上海:1949年以来的建筑、城市与文化
  • 大理杨徐邱再审上诉案宣判:驳回上诉,维持再审一审判决