【typenum】 16 无符号整数标记

一、源码

这段代码是 Rust 中用于实现编译时无符号整数的核心部分。它定义了一个 Unsigned trait 并为两种类型实现了该 trait:UTerm(表示零)和 UInt<U, B>(表示非零数字)。

  1. 定义(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;
}
  1. 实现
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

这相当于:

  1. 将高位部分 U 左移一位(相当于乘以 2)
  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 泛型编程和类型系统强大能力的典型体现。

八、改进建议

  1. 移除 #[cfg(feature = “i128”)]
    由于 Rust 1.26+ 已经稳定支持 i128/u128,不再需要 feature gate 来控制其可用性,可以移除所有 #[cfg(feature = “i128”)] 条件编译。
  2. 合并 Unsigned 相关定义到 uint.rs
    UInt 结构体(表示非零无符号整数)和 UTerm(表示零)都是用于无符号整数计算,可以将它们的定义、Unsigned trait 及其实现放在同一个文件(如 uint.rs),而不是分散在 marker_traits.rs 和 uint.rs 中。
    其优点:
  • 减少文件跳转,提高可读性

  • 逻辑相关的代码放在一起,便于维护

  • 更符合 Rust 的模块化设计(类型 + trait + 实现放在一起)

  1. 提供 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()
  1. 为 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>;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

liuyuan77

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值