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

结合场景再看三个关注点

  1. 同一函数服务多类型
  2. 返回 &T 不搬出所有权
  3. Ord bound> 合法

核心概念与准确模型

泛型结构体/枚举

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。

工程实践

  1. 先写具体类型,再抽取泛型
  2. bound 尽量小
  3. 公共 API 稳定后谨慎增加参数
  4. 性能敏感处对比 dyn 与泛型

本节总结

  • 类型参数 + bound
  • 单态化是默认实现策略教学模型
  • 与 trait 一体

自测题

  1. 为什么 fn id<T>(x: T) -> T { x } 不需要 bound?
  2. 单态化的主要代价是什么?
参考答案
  1. 只移动值,不调用 T 的方法。
  2. 可能的代码膨胀与编译时间。

延伸阅读与资料来源

资料类型支撑内容
The Book — Generic Types官方书泛型
The Book — Performance of Monomorphization官方书相关讨论
创建于 2026/7/15 更新于 2026/7/15