Rust学习笔记:泛型与Trait——写出可复用的Rust代码

写完前七篇,你应该能写出一段功能完整的 Rust 代码了。但如果想写可复用的代码——同一个函数处理不同类型、同一个结构体适配多种数据——就需要泛型和 Trait。这两个概念在 C 里对应的是 void* + 函数指针和虚函数表,但在 Rust 里更安全、更直观。


1. 泛型:一套代码,多种类型

1.1 泛型函数

不用泛型的话,求最大值的函数要写好几份:

fn largest_i32(list: &[i32]) -> i32 { /* ... */ }
fn largest_char(list: &[char]) -> char { /* ... */ }

用泛型,一份搞定:

fn largest<T: PartialOrd + Copy>(list: &[T]) -> T {
    let mut largest = list[0];
    for &item in list {
        if item > largest {
            largest = item;
        }
    }
    largest
}

let nums = vec![34, 50, 25, 100, 65];
println!("最大数: {}", largest(&nums));   // 100

let chars = vec!['y', 'm', 'a', 'q'];
println!("最大字符: {}", largest(&chars)); // y

T 是类型参数。T: PartialOrd + Copy 是说「T 必须能比较大小(PartialOrd)且能自动复制(Copy)」。这叫 Trait Bound——编译器在调用时就检查传入的类型是否满足这些约束。

1.2 泛型结构体

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

let p1 = Point { x: 5, y: 10 };    // Point<i32>
let p2 = Point { x: 3.0, y: 4.0 }; // Point<f64>

泛型方法:

impl<T: std::fmt::Display> Point<T> {
    fn display(&self) -> String {
        format!("({}, {})", self.x, self.y)
    }
}

还可以为特定类型写专用方法:

impl Point<f64> {
    fn distance_from_origin(&self) -> f64 {
        (self.x.powi(2) + self.y.powi(2)).sqrt()
    }
}

let p = Point { x: 3.0, y: 4.0 };
p.distance_from_origin(); // 5.0 —— 只有 Point<f64> 有这个功能

2. Trait:共享行为的接口

Trait 定义了类型「能干什么」。C 程序员可以理解为带类型检查的虚函数表。

2.1 定义与实现

trait Summary {
    fn summarize(&self) -> String;
}

struct Article {
    title: String,
    content: String,
}

impl Summary for Article {
    fn summarize(&self) -> String {
        format!("《{}》:{}…", self.title, self.content)
    }
}

一个类型可以实现多个 Trait,一个 Trait 也可以被多个类型实现——完全解耦。

2.2 Trait 作为函数参数

fn notify(item: &impl Summary) {
    println!("通知: {}", item.summarize());
}

impl Summary 表示「任何实现了 Summary 的类型都可以传进来」。语法上等价于泛型写法 fn notify<T: Summary>(item: &T),但更简洁。

2.3 多重约束

fn largest<T: PartialOrd + Copy + Summary>(list: &[T]) -> &T {
    // T 必须同时满足三个约束
}

3. derive:自动实现常见 Trait

很多 Trait 的实现是机械的样板代码,Rust 可以用 #[derive(...)] 自动生成:

#[derive(Debug, Clone, PartialEq)]
struct Book {
    title: String,
    pages: u32,
}

let b1 = Book { title: String::from("Rust Book"), pages: 500 };
let b2 = b1.clone();
println!("{:?}", b1);           // Debug:打印结构体
println!("{}", b1 == b2);       // PartialEq:比较相等

常用的 derive:

derive 提供的能力
Debug {:?} 格式化打印,调试必备
Clone .clone() 深拷贝
Copy 赋值时自动 Copy 而非移动(仅栈上数据)
PartialEq ==!= 比较
Default Default::default() 创建默认值

4. 为什么 Rust 的泛型更快

C 语言实现泛型靠 void* + 强制转换,类型检查靠程序员自觉。C++ 模板是编译期代码生成(单态化),膨胀但快。Java/C# 靠类型擦除 + 装箱,有运行时开销。

Rust 走的是编译期单态化路线:编译器为每个具体类型生成一份专用代码,运行时零开销。用 C 的话说:等于编译器帮你手写了 largest_i32largest_charlargest_f64 三份函数——但源码你只写了一次。


总结

概念 作用 C 对应物
T 泛型 一套代码处理多种类型 void* + 宏
Trait 定义类型必须实现的行为 函数指针表 / 虚表
impl Trait 函数参数接受任何实现了该 Trait 的类型 void* + 运行时类型检查
#[derive(...)] 自动生成样板 Trait 实现

泛型 + Trait 是 Rust 类型系统的顶点。掌握它们之后,剩下的就是看标准库文档、写项目和踩坑了。

下一篇进入模块系统modusepub,开始把代码分到多个文件里。

posted @ 2026-09-19 09:47  PC2005-cloud  阅读(3)  评论(0)    收藏  举报