一、泛型概念和基本用法(泛型函数 / 结构体 / 枚举)

专业名词释义

  • 泛型(Generics):用类型参数(Type Parameter)让同一份代码适用于多种具体类型的技术。Rust 在编译期通过**单态化(Monomorphization)**将泛型代码展开为具体类型版本,实现零成本抽象。
  • 泛型参数:惯例用 TUE 表示,声明位置在 fnstructenum 后的 < > 中。

用法示例(泛型函数):

fn display_number<T: std::fmt::Debug>(num: T) {
    println!("The number is {:?}", num);
}
fn main() {
    display_number(-1i32);
    display_number(2u64);
    display_number(3.14f32);
}

用法示例(泛型结构体 + 方法):

struct Point<T, U> {
    x: T,
    y: U,
}

struct GenVal<T> {
    gen_val: T,
}

impl<T> GenVal<T> {
    fn value(&self) -> &T {
        &self.gen_val
    }
}

用法示例(泛型枚举):

enum Option<T> { Some(T), None }
enum Result<T, E> { Ok(T), Err(E) }

注意事项与最佳实践

  • 泛型参数必须在使用前声明,否则编译错误。
  • 深度提示:单态化会为每个具体类型生成独立代码(二进制体积增大但运行时零开销)。
  • 最佳实践:小项目优先具体类型函数,大项目用泛型 + #[derive(...)]TU 命名惯例,多个参数时按出现顺序声明。

二、泛型约束(Trait Bound 与 where 子句)

专业名词释义

  • Trait Bound(泛型约束)T: Trait1 + Trait2,限定泛型必须实现指定 trait,才能调用其方法。
  • where 子句:把约束移到签名后,提升可读性,支持复杂约束。

用法示例(Trait Bound):

use std::fmt::Debug;
fn display<T: Debug>(val: T) {
    println!("{:?}", val);
}

// 多个参数、多个 bound
fn display2<T1: Debug, T2: Debug>(v1: T1, v2: T2) { ... }

// 多个 trait
fn display3<T: Debug + Clone>(val: T) {
    println!("{:?}", val.clone());
}

用法示例(where 子句):

fn display<T>(val: T) where T: std::fmt::Display + Debug {
    println!("{}", val);
}

struct A<T> where T: Clone + Default {
    val: T,
}

trait TraitA<T> where T: Copy {}

注意事项与最佳实践

  • 约束越紧,编译器能提供的 API 越多(可调用 trait 方法)。
  • 深度提示where 支持 for<'a>、关联类型约束、?Sized 等复杂写法,是现代 Rust 首选。
  • 最佳实践:简单约束用 T: Trait,复杂/多行用 where;避免过度约束导致调用方难以满足。

三、泛型生命周期参数

专业名词释义

  • 泛型生命周期:把 'a 'b 当作泛型参数声明,用于标注引用有效期。生命周期参数始终写在类型参数之前

用法示例(含生命周期的结构体):

struct S<'a> {
    p: &'a [u32],
}

struct S2<'a, T> {          // 生命周期在前
    p: &'a [T],
}

用法示例(函数):

fn foo<'a, 'b>(array1: &'a [u32], array2: &'b [i64]) {
    let s1 = S { p: array1 };
    let s2 = S { p: array2 };
}

注意事项与最佳实践

  • 生命周期是借用检查器的工具,防止悬垂引用。
  • 深度提示:多个生命周期允许结构体成员引用不同作用域;省略规则(elision)在简单函数中自动生效。
  • 最佳实践:结构体含引用时必须加 'a;复杂场景结合 Cow<'a, T> 减少显式标注。

四、泛型关联类型(GAT)

专业名词释义

  • Generic Associated Types (GAT):在 trait 的关联类型中再带泛型参数(type I<T>),让一个类型能表达“随参数变化”的关联类型。Rust 1.65+ 稳定。

用法示例(经典 Calculator):

trait Calculator {
    type I<T>: std::ops::Add<T, Output = Self::I<T>>
    where
        T: std::ops::Add<T, Output = T>;
}

struct A<T> { val: T }
impl<T: std::ops::Add<T, Output = T>> std::ops::Add<T> for A<T> {
    type Output = A<T>;
    fn add(self, rhs: T) -> Self::Output {
        A { val: self.val + rhs }
    }
}

// 枚举同理...
struct Data<T: Calculator> {
    data1: T::I<i8>,
    data2: T::I<u16>,
    data3: T::I<f32>,
}

注意事项与最佳实践

  • 没有 GAT 时必须声明多个独立泛型参数,无法表达“同一种声明类型”。
  • 深度提示:GAT 解决“关联类型带泛型”的痛点,常用于异步、迭代器、 lending 等高级抽象。
  • 最佳实践:GAT + where 约束最常见;实际项目中常搭配 impl Trait 返回值使用。

五、泛型使用场景(类型别名)

专业名词释义

  • 类型别名(Type Alias)type NewName<T> = OldType<T, ...>; 为复杂泛型类型起一个简短名字。

用法示例

type IOError<T> = Result<T, std::io::Error>;

fn foo() -> IOError<String> {
    // ...
}

注意事项与最佳实践

  • 极大简化函数签名、结构体字段,尤其在 Result<Option<T>, E> 等嵌套场景。
  • 深度提示:别名不创建新类型(仍是原类型),可用于 impl 块。
  • 最佳实践:公共 API 中用别名提升可读性;结合 GAT 使用更强大。

本章小结 + 进阶练习

学完本章你应该能做到

  • 熟练定义和使用泛型函数、结构体、枚举
  • 掌握 trait bound、where 子句、生命周期参数的组合写法
  • 理解并应用泛型关联类型(GAT)
  • 使用类型别名简化复杂泛型签名

进阶练习(建议立刻敲代码)

  1. 实现一个泛型 struct Stack<T>(push/pop),用 Vec<T> 存储。
  2. 为自定义 Point<T> 实现 Add trait(支持 T: Add<Output=T>)。
  3. 用 GAT 定义 trait Storagetype Item<'a>),让不同容器返回不同借用类型。
  4. 写一个 fn longest<'a, T>(x: &'a T, y: &'a T) -> &'a T where T: PartialOrd 返回较长的值。
  5. 用类型别名 type MyResult<T> = Result<T, Box<dyn std::error::Error>>; 重构一个错误处理函数。

泛型 + Trait + 生命周期 = Rust 零成本抽象的灵魂。掌握它,你就能写出高度复用、安全、性能极致的代码!

(完)

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐