Rust 泛型
类型参数如何抽象函数与结构体;trait bound 约束;单态化与成本边界。
#type / concept
#status / growing
#tech / dev
#resource / rust
[!info] 关联笔记
Rust 泛型
这个概念为什么出现
不想为 i32/String/UserId 各写一遍相同算法。
泛型把“类型”参数化,再配合 trait bound 限制“必须具备的能力”。
[!abstract] 一句话理解 泛型是类型参数;
T: Trait限制可用操作;编译器通常通过单态化为每个具体类型生成专用代码,换运行时效率。
最小可运行示例
场景:库存服务——在任意可比较的 ID 列表中找最大值
SKU 数、仓库优先级等都要“找最大”;元素只需 Ord。
// 业务意图:在非空切片中找最大元素。
// 教学点:泛型函数 + Ord bound;返回引用避免 move。
fn max_in<T: Ord>(items: &[T]) -> Option<&T> {
let mut best = items.get(0)?;
for item in items.iter().skip(1) {
if item > best {
best = item;
}
}
Some(best)
}
fn main() {
let stock = [3, 9, 4, 9, 1];
println!("max_stock={}", max_in(&stock).unwrap());
let names = ["sku-b", "sku-a", "sku-c"];
println!("max_name={}", max_in(&names).unwrap());
}
建议运行:cargo run
期望输出:
max_stock=9
max_name=sku-c
结合场景再看三个关注点
- 同一函数服务多类型
- 返回
&T不搬出所有权 Ordbound 让>合法
核心概念与准确模型
泛型结构体/枚举
struct Wrapper<T> { value: T }
enum Maybe<T> { Some(T), None }
约束与 where
fn print_it<T>(x: T) where T: std::fmt::Display {
println!("{x}");
}
单态化
编译器为 max_in::<i32> 与 max_in::<&str> 生成不同版本(概念模型)。
代价:代码体积可能上升;收益:无虚调用。
与生命周期参数
<'a, T> 可同时出现,见 生命周期。
设计动机
- 抽象而不放弃类型安全
- 零成本(相对动态分发)
- 标准库大量建立在泛型上(
Option/Result/Vec)
边界与误区
- 过度泛型损害可读性
- 推断失败时补注解
- 泛型方法与 trait 对象对象安全互相牵扯
[!warning] 常见误区:无 bound 却对 T 调用方法 编译器不知道
T有哪些方法;加 trait bound。
工程实践
- 先写具体类型,再抽取泛型
- bound 尽量小
- 公共 API 稳定后谨慎增加参数
- 性能敏感处对比
dyn与泛型
本节总结
- 类型参数 + bound
- 单态化是默认实现策略教学模型
- 与 trait 一体
自测题
- 为什么
fn id<T>(x: T) -> T { x }不需要 bound? - 单态化的主要代价是什么?
参考答案
- 只移动值,不调用 T 的方法。
- 可能的代码膨胀与编译时间。
延伸阅读与资料来源
| 资料 | 类型 | 支撑内容 |
|---|---|---|
| The Book — Generic Types | 官方书 | 泛型 |
| The Book — Performance of Monomorphization | 官方书 | 相关讨论 |