本页目录
泛型与单态化
Rust 泛型的核心机制是单态化——编译期为每种具体类型参数生成一份独立的机器码。这意味着
Vec<i32>和Vec<String>是两套完全不同的函数体,零运行时开销,代价是二进制膨胀。const generics 把常量值也纳入泛型参数,specialization 允许为特定类型提供更优实现(目前 nightly only)。
Monomorphization: 编译期为每个具体类型生成代码
Rust 泛型不是运行时的多态(没有 vtable)——编译器对每个使用泛型的具体类型生成独立的机器码:
let _ = max; // 编译器生成 max_i32
let _ = max; // 编译器生成 max_f64
let x = "a"; let y = "b";
let _ = max; // 编译器生成 max_&str
// 输出 binary 中有 3 份独立的 max 函数, 每份针对具体类型优化
这与 C++ 模板的机制相同——都是编译期代码生成。但在 Rust 中,泛型在定义时就被检查(不需要等到实例化),所以错误信息更清晰。C++ 模板的错误通常在实例化时爆出,导致几十行的模板错误。
"零成本抽象"的含义
release build 中,泛型函数通常被内联到调用点——最终汇编中看不到泛型函数的存在,只剩几条直接操作 i32 或 f64 的 cmp 指令。这就是 Rust 所谓的"zero-cost abstraction":你能用高层抽象,但编译后的代码和手写针对具体类型的代码一样快。
代码膨胀与缓解
每个具体类型生成一份代码 → 大量类型组合 → 编译时间爆炸 + binary 膨胀。经典缓解策略:提取非泛型 inner 函数:
// 不依赖 T → 不分叉
关键:把必须泛型的部分(类型转换)和可以用 trait object 或具体类型处理的部分(业务逻辑)分离。
Const Generics: 值作为泛型参数
传统泛型参数是类型。const generics (Rust 1.51+) 让值也成为泛型参数——编译期常量:
let b1: = Buffer ;
let b2: = Buffer ;
// b1 和 b2 是**不同类型**! 不能互相赋值
主要用途:固定大小的 array/vector、SIMD lanes、编译期 assert array size。
Specialization (nightly, 尚未 stable)
当前 stable Rust 不支持 trait specialization——你不能为一个 trait 提供"更具体的 impl 覆盖更泛的 impl"。这限制了某些性能优化(如为 Vec<u8> 提供比泛型 Vec<T> 更高效的 Extend impl)。nightly 上有 min_specialization 可用,但 stable 上还没有。目前替代方案是用 newtype pattern 提供特化。
参考
- Rust Book: Chapter 10.1
- Rust Reference: Const generics
- RFC 2000: Const generics; RFC 1210: Specialization
Keywords: monomorphization, zero-cost abstraction, code bloat, const generics, specialization