
泛型(Generics)让我们只写一份函数/结构体/类型代码,就可以适配不同的具体类型,实现高复用、类型安全和零成本抽象。Rust泛型所有替换会在编译期间单态化,不会有运行时损耗。
泛型函数通过在函数名后加<T>等参数,使其可以处理多种类型。
fn max<T: PartialOrd>(a: T, b: T) -> T {
if a > b { a } else { b }
}
fn main() {
println!("{}", max(8, 19)); // i32
println!("{}", max(1.58, 2.3)); // f64
println!("{}", max('a', 'z')); // char
}<T: PartialOrd>要求T支持>等比较操作(trait bound)。
struct Point<T> {
x: T,
y: T,
}
let p1 = Point { x: 1, y: 2 };
let p2 = Point { x: 1.5, y: -2.3 };枚举同理:
enum Option<T> {
Some(T),
None,
}泛型可以在impl块、方法层单独出现:
struct Stack<T> { data: Vec<T> }
impl<T> Stack<T> {
fn new() -> Self { Stack { data: Vec::new() } }
fn push(&mut self, v: T) { self.data.push(v); }
fn pop(&mut self) -> Option<T> { self.data.pop() }
}支持多个泛型参数及复合约束:
struct Pair<T, U> { left: T, right: U }
fn show<T: std::fmt::Debug + Clone>(x: T) { println!("{:?}", x); }where简化复杂约束:
fn calc<T, U>(a: T, b: U) -> i32 where T: Copy + From<U>, U: Copy { a.into() + b.into() }fn max(a: T, b: T) -> T { ... } :未加T: PartialOrd,无法比较let v: Vec<T>; :不允许未定泛型,必须为Vec<i32>或Vec<T>在泛型结构体中+:应写T: Clone + Debug修正版:
fn max<T: PartialOrd>(a: T, b: T) -> T { if a > b { a } else { b } }实现一个 swap(a: &mut T, b: &mut T) 函数,交换变量值:
fn swap<T>(a: &mut T, b: &mut T) {
let tmp = std::mem::replace(a, std::mem::replace(b, unsafe { std::mem::zeroed() }));
*b = tmp;
}标准答案可用std::mem::swap。
写一个泛型统计最大值函数:
fn max_of_three<T: Ord>(a: T, b: T, c: T) -> T {
let ab = if a > b { a } else { b };
if ab > c { ab } else { c }
}编写泛型Vec2,实现求模方法:
struct Vec2<T> { x: T, y: T }
impl Vec2<f64> { fn norm(&self) -> f64 { (self.x*self.x + self.y*self.y).sqrt() } }场景/语法 | 写法示例 | 关键点与规则 | 常见坑与修正 |
|---|---|---|---|
泛型函数 | fn max<T: PartialOrd>(a: T, b: T) -> T | 在函数名后写 <T>;用 trait bound 限定能力 | 忘记加 bound 无法比较;加上 T: PartialOrd |
多参数泛型 | fn pair<T, U>(a: T, b: U) | 多个类型参数用逗号分隔 | 不必要时别滥加类型参数 |
结构体泛型 | struct Point<T> { x: T, y: T } | 结构体字段可复用同一类型参数 | 实例化时要具体:Point<i32> |
枚举泛型 | enum Opt<T> { Some(T), None } | 适配多种承载类型 | 与标准库 Option<T> 类似 |
impl 泛型 | impl<T> Stack<T> { fn new() -> Self { … } } | 在 impl 处声明 <T> | 忘了写 <T> 会报错 |
方法级泛型 | impl<T> S<T> { fn map<U>(&self, f: fn(T)->U) -> U {…} } | 方法可再引入新的 U | 方法签名里写 <U> |
trait 约束(bound) | fn f<T: Debug + Clone>(x: T) | 多约束用 + 连接 | 写成 T: Debug, Clone 错,应 T: Debug + Clone |
where 子句 | fn g<T, U>(..) where T: Clone, U: Debug | 复杂约束更清晰 | 推荐用于长签名可读性 |
派生/实现约束 | #[derive(Clone, Debug)] struct S<T> where T: Clone + Debug | T 要实现相应 trait 才能派生 | 忽略 where 导致无法派生 |
单态化 | 编译期为每个实参类型生成专用版本 | 零运行时开销 | 过度泛型化可能增大二进制 |
关联类型(进阶) | trait Iterator { type Item; } | 将结果类型写成关联类型 | 与泛型参数各有优劣 |
返回值使用泛型 | fn id<T>(x: T) -> T | 返回与输入同类型 | 不能推导“不同类型”返回 |
常见错:未具体化 | let v: Vec<T> = vec![]; | 局部变量不能裸 T | 写 Vec<i32> 或处于泛型上下文 |
常见错:缺少 bound | fn sum<T>(a: T, b: T) -> T { a + b } | + 需要 T: Add<Output=T> | fn sum<T: Add<Output=T>>(..) |
常见错:约束冲突 | 同一 T 既要 Copy 又需非 Copy 行为 | 重新设计/拆分 API | 用引用/克隆或拆函数 |
下一节 将全面讲解trait,用于打造强健的行为抽象和多态。