【typenum】 17 非负数标记
一、源码
这段代码是 Rust 中用于实现编译时无符号整数的核心部分。它定义了一个 Unsigned trait 并为两种类型实现了该 trait:UTerm(表示零)和 UInt<U, B>(表示非零数字)。
- 定义(marker_traits.rs)
/// The **marker trait** for compile time unsigned integers.
///
/// # Example
/// ```rust
/// use typenum::{Unsigned, U3};
///
/// assert_eq!(U3::to_u32(), 3);
/// assert_eq!(U3::I32, 3);
/// ```
pub trait Unsigned: Sealed + Copy + Default + 'static {#[allow(missing_docs)]const U8: u8;#[allow(missing_docs)]const U16: u16;#[allow(missing_docs)]const U32: u32;#[allow(missing_docs)]const U64: u64;#[cfg(feature = "i128")]#[allow(missing_docs)]const U128: u128;#[allow(missing_docs)]const USIZE: usize;#[allow(missing_docs)]const I8: i8;#[allow(missing_docs)]const I16: i16;#[allow(missing_docs)]const I32: i32;#[allow(missing_docs)]const I64: i64;#[cfg(feature = "i128")]#[allow(missing_docs)]const I128: i128;#[allow(missing_docs)]const ISIZE: isize;#[allow(missing_docs)]fn to_u8() -> u8;#[allow(missing_docs)]fn to_u16() -> u16;#[allow(missing_docs)]fn to_u32() -> u32;#[allow(missing_docs)]fn to_u64() -> u64;#[cfg(feature = "i128")]#[allow(missing_docs)]fn to_u128() -> u128;#[allow(missing_docs)]fn to_usize() -> usize;#[allow(missing_docs)]fn to_i8() -> i8;#[allow(missing_docs)]fn to_i16() -> i16;#[allow(missing_docs)]fn to_i32() -> i32;#[allow(missing_docs)]fn to_i64() -> i64;#[cfg(feature = "i128")]#[allow(missing_docs)]fn to_i128() -> i128;#[allow(missing_docs)]fn to_isize() -> isize;
}
- 实现
impl Unsigned for UTerm {const U8: u8 = 0;const U16: u16 = 0;const U32: u32 = 0;const U64: u64 = 0;#[cfg(feature = "i128")]const U128: u128 = 0;const USIZE: usize = 0;const I8: i8 = 0;const I16: i16 = 0;const I32: i32 = 0;const I64: i64 = 0;#[cfg(feature = "i128")]const I128: i128 = 0;const ISIZE: isize = 0;#[inline]fn to_u8() -> u8 {0}#[inline]fn to_u16() -> u16 {0}#[inline]fn to_u32() -> u32 {0}#[inline]fn to_u64() -> u64 {0}#[cfg(feature = "i128")]#[inline]fn to_u128() -> u128 {0}#[inline]fn to_usize() -> usize {0}#[inline]fn to_i8() -> i8 {0}#[inline]fn to_i16() -> i16 {0}#[inline]fn to_i32() -> i32 {0}#[inline]fn to_i64() -> i64 {0}#[cfg(feature = "i128")]#[inline]fn to_i128() -> i128 {0}#[inline]fn to_isize() -> isize {0}
}
impl<U: Unsigned, B: Bit> Unsigned for UInt<U, B> {const U8: u8 = B::U8 | U::U8 << 1;const U16: u16 = B::U8 as u16 | U::U16 << 1;const U32: u32 = B::U8 as u32 | U::U32 << 1;const U64: u64 = B::U8 as u64 | U::U64 << 1;#[cfg(feature = "i128")]const U128: u128 = B::U8 as u128 | U::U128 << 1;const USIZE: usize = B::U8 as usize | U::USIZE << 1;const I8: i8 = B::U8 as i8 | U::I8 << 1;const I16: i16 = B::U8 as i16 | U::I16 << 1;const I32: i32 = B::U8 as i32 | U::I32 << 1;const I64: i64 = B::U8 as i64 | U::I64 << 1;#[cfg(feature = "i128")]const I128: i128 = B::U8 as i128 | U::I128 << 1;const ISIZE: isize = B::U8 as isize | U::ISIZE << 1;#[inline]fn to_u8() -> u8 {B::to_u8() | U::to_u8() << 1}#[inline]fn to_u16() -> u16 {u16::from(B::to_u8()) | U::to_u16() << 1}#[inline]fn to_u32() -> u32 {u32::from(B::to_u8()) | U::to_u32() << 1}#[inline]fn to_u64() -> u64 {u64::from(B::to_u8()) | U::to_u64() << 1}#[cfg(feature = "i128")]#[inline]fn to_u128() -> u128 {u128::from(B::to_u8()) | U::to_u128() << 1}#[inline]fn to_usize() -> usize {usize::from(B::to_u8()) | U::to_usize() << 1}#[inline]fn to_i8() -> i8 {B::to_u8() as i8 | U::to_i8() << 1}#[inline]fn to_i16() -> i16 {i16::from(B::to_u8()) | U::to_i16() << 1}#[inline]fn to_i32() -> i32 {i32::from(B::to_u8()) | U::to_i32() << 1}#[inline]fn to_i64() -> i64 {i64::from(B::to_u8()) | U::to_i64() << 1}#[cfg(feature = "i128")]#[inline]fn to_i128() -> i128 {i128::from(B::to_u8()) | U::to_i128() << 1}#[inline]fn to_isize() -> isize {B::to_u8() as isize | U::to_isize() << 1}
}
二、Unsigned Trait 定义
Unsigned 是一个标记 trait(marker trait),用于表示编译时的无符号整数。它要求实现者必须:
-
实现 Sealed trait(防止外部实现)
-
实现 Copy 和 Default
-
具有 'static 生命周期
它定义了一系列关联常量和方法,用于将编译时数字转换为运行时数字:
关联常量:
-
U8, U16, U32, U64, U128, USIZE:对应各种无符号整数类型的值
-
I8, I16, I32, I64, I128, ISIZE:对应各种有符号整数类型的值
方法:
- to_u8(), to_u16(), …, to_isize():返回对应类型的值
三、UTerm 的实现
UTerm 表示数字 0,所以所有关联常量和方法都返回 0。
四、UInt<U, B> 的实现
UInt<U, B> 是一个类型级别的数字表示,其中:
-
U 是一个 Unsigned 类型,表示高位部分
-
B 是一个 Bit 类型(可以是 B0 或 B1),表示最低位
这种表示方法实际上是二进制表示。例如:
-
UInt<UTerm, B1> 表示 1
-
UInt<UInt<UTerm, B1>, B0> 表示 10(二进制)即 2(十进制)
实现细节:
对于每个关联常量和方法,计算方式都是:
B::U8 | U::U8 << 1
这相当于:
- 将高位部分 U 左移一位(相当于乘以 2)
- 加上最低位 B 的值
例如,对于数字 3(二进制 11):
-
表示为 UInt<UInt<UTerm, B1>, B1>
-
计算 U8:B1::U8 | UInt<UTerm, B1>::U8 << 1 = 1 | (1 << 1) = 1 | 2 = 3
五、示例解释
文档中的示例:
use typenum::{Unsigned, U3};assert_eq!(U3::to_u32(), 3);
assert_eq!(U3::I32, 3);
-
U3 实际上是 UInt<UInt<UTerm, B1>, B1>
-
当调用 to_u32() 或访问 I32 时,会递归计算得到值 3
六、特点
这种设计允许在编译时进行数值计算,常用于:
-
数组长度的类型级验证
-
矩阵维度的类型级检查
-
其他需要编译时数值计算的场景
七、性能
由于所有计算都在编译时完成,运行时没有任何开销。生成的代码就像直接使用了常量一样高效。
这种类型级别的数字表示是 Rust 泛型编程和类型系统强大能力的典型体现。
八、改进建议
- 移除 #[cfg(feature = “i128”)]
由于 Rust 1.26+ 已经稳定支持 i128/u128,不再需要 feature gate 来控制其可用性,可以移除所有 #[cfg(feature = “i128”)] 条件编译。 - 合并 Unsigned 相关定义到 uint.rs
UInt 结构体(表示非零无符号整数)和 UTerm(表示零)都是用于无符号整数计算,可以将它们的定义、Unsigned trait 及其实现放在同一个文件(如 uint.rs),而不是分散在 marker_traits.rs 和 uint.rs 中。
其优点:
-
减少文件跳转,提高可读性
-
逻辑相关的代码放在一起,便于维护
-
更符合 Rust 的模块化设计(类型 + trait + 实现放在一起)
- 提供 From 转换
允许从 UTerm 和 UInt 转换到基本整数类型,方便使用:
impl From<UTerm> for u8 {fn from(_: UTerm) -> u8 { 0 }
}impl<U: Unsigned, B: Bit> From<UInt<U, B>> for u8 {fn from(n: UInt<U, B>) -> u8 {B::U8 | U::to_u8() << 1}
}
这样用户可以直接:
let x: u8 = U3::into(); // 而不是 U3::to_u8()
- 为 Unsigned 增加 ZERO 和 ONE 常量
由于无符号整数一定有 0 和 1,可以在 trait 中定义这两个常量,方便通用代码使用:
pub trait Unsigned: Sealed + Copy + Default + 'static {const ZERO: Self;const ONE: Self;// ...
}
然后分别在 UTerm 和 UInt 中实现:
impl Unsigned for UTerm {const ZERO: Self = UTerm;const ONE: Self = UInt<UTerm, B1>; // 需要确保 UInt<UTerm, B1> 是 1
}impl<U: Unsigned, B: Bit> Unsigned for UInt<U, B> {const ZERO: Self = UTerm; // 可能需要调整,或提供 From<UTerm>const ONE: Self = UInt<UTerm, B1>;
}