Skip to content

Day 11: 泛型基础

泛型(Generic)是编程语言中一种强大的抽象机制,允许你编写可适用于多种类型的代码,而无需重复编写相似的逻辑。在Rust中,泛型在编译期通过一种称为**单态化(Monomorphization)**的过程进行静态分发,这意味着泛型代码不会产生运行时开销。

泛型的核心优势在于:

  • 代码复用:一次编写,适用于多种类型
  • 类型安全:编译期检查,防止类型错误
  • 零成本抽象:通过单态化,运行时性能与手动特化代码无异

让我们从泛型函数开始。以下是一个简单的泛型函数,用于返回两个值中的较大者:

fn largest<T: PartialOrd>(list: &[T]) -> &T {
let mut largest = &list[0];
for item in list {
if item > largest {
largest = item;
}
}
largest
}
fn main() {
let numbers = vec![34, 50, 25, 100, 65];
let result = largest(&numbers);
println!("最大值为: {}", result);
let chars = vec!['y', 'm', 'a', 'q'];
let result = largest(&chars);
println!("最大的字符是: {}", result);
}

注意函数签名中的 <T: PartialOrd>,这是trait约束(trait bound)的语法,表示 T 必须实现 PartialOrd trait 才能使用此函数。我们将在明天的教程中深入学习trait。

泛型也可以用于结构体定义:

struct Point<T> {
x: T,
y: T,
}
struct MixedPoint<T, U> {
x: T,
y: U,
}
fn main() {
let integer_point = Point { x: 5, y: 10 };
let float_point = Point { x: 1.0, y: 4.0 };
let mixed = MixedPoint { x: 5, y: 4.0 };
println!("整数点: ({}, {})", integer_point.x, integer_point.y);
println!("浮点点: ({}, {})", float_point.x, float_point.y);
println!("混合点: ({}, {})", mixed.x, mixed.y);
}

Point<T> 结构体有两个类型相同的字段,而 MixedPoint<T, U> 允许字段类型不同。

枚举也可以使用泛型,这在标准库中有许多经典案例,例如 Option<T>:

enum Option<T> {
Some(T),
None,
}
enum Result<T, E> {
Ok(T),
Err(E),
}
fn main() {
let some_number: Option<i32> = Some(5);
let some_string: Option<&str> = Some("a string");
let no_number: Option<i32> = None;
let ok_result: Result<i32, &str> = Ok(5);
let err_result: Result<i32, &str> = Err("error message");
}

Option<T> 是Rust中最常用的枚举之一,用于表示值可能存在或不存在的情况。Result<T, E> 用于错误处理。

理解泛型的实现原理很重要。Rust编译器采用单态化策略处理泛型,这意味着:

每当使用泛型函数或类型时,编译器会生成一份该泛型代码的具体类型特化版本。

以 largest 函数为例:

  • 当传入 &[i32] 时,编译器生成 largest_i32 版本
  • 当传入 &[char] 时,编译器生成 largest_char 版本

这意味着:

  • 优点:零运行时开销,性能等同于手写的特化代码
  • 缺点:代码膨胀(code bloat),每种类型组合都会生成一份实例
// 编译器实际生成的代码类似于:
fn largest_i32(list: &[i32]) -> &i32 { /* ... */ }
fn largest_char(list: &[char]) -> &char { /* ... */ }

为泛型结构体或枚举实现方法时,需要在 impl 块中声明类型参数:

struct Point<T> {
x: T,
y: T,
}
impl<T> Point<T> {
fn new(x: T, y: T) -> Self {
Point { x, y }
}
fn x(&self) -> &T {
&self.x
}
}
// 为特定类型的 Point 实现方法
impl Point<f64> {
fn distance_from_origin(&self) -> f64 {
(self.x.powi(2) + self.y.powi(2)).sqrt()
}
}
fn main() {
let p = Point::new(5, 10);
println!("p.x = {}", p.x());
}

今天我们学习了Rust泛型编程的基础:

  • 泛型函数:通过类型参数 <T> 编写适用于多种类型的函数
  • 泛型结构体和枚举:使用泛型定义可复用的数据结构
  • Monomorphization:编译器将泛型代码静态生成为具体类型的特化版本,实现零成本抽象
  • 泛型方法:在 impl 块中使用泛型参数实现方法

泛型是Rust抽象层次编程的核心工具,明天我们将学习trait系统,它与泛型紧密配合,构成了Rust类型系统的基石。