迭代器、泛型、trait 与 API 设计
本章目标
这一章把前面造轮子时用到的抽象串起来:迭代器表达遍历,泛型表达类型参数,trait 表达能力约束,API 设计表达你希望调用者怎么使用代码。
它是什么
迭代器是逐个产生值的对象。trait 是一组行为约定。泛型让代码适用于多种类型。
fn print_all<T: std::fmt::Display>(items: &[T]) {
for item in items {
println!("{item}");
}
}
T: Display 表示元素必须能被格式化显示。
为什么需要
没有泛型,你要为 Stack<i32>、Stack<String> 写两套代码。没有 trait,你无法表达“这个类型必须能比较”“这个类型必须能哈希”“这个类型必须能显示”。
Rust 的标准库大量使用这些抽象,让代码既通用又静态安全。
怎么使用
为自定义类型实现 trait:
struct Chapter {
title: String,
}
impl std::fmt::Display for Chapter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.title)
}
}
实现后,Chapter 就可以用 {} 打印。
逐行解释
fn find_first<T, F>(items: &[T], mut predicate: F) -> Option<&T>
where
F: FnMut(&T) -> bool,
{
for item in items {
if predicate(item) {
return Some(item);
}
}
None
}
T是元素类型。F是函数或闭包类型。items: &[T]借用一段元素切片。mut predicate: F表示闭包调用时可能修改自己捕获的状态。where F: FnMut(&T) -> bool约束闭包能接收&T并返回布尔值。for item in items遍历切片。- 命中条件时返回
Some(item)。 - 全部不命中返回
None。
这个函数展示了 API 设计的力量:调用者决定匹配条件,函数负责遍历流程。
常见坑
- 泛型不是越多越好。先从具体类型写清楚,再提取共性。
- trait bound 应该放在真正需要能力的地方。
- 返回迭代器时可能需要
impl Iterator<Item = T>。 - API 参数能借用就不要强行拿所有权,除非函数确实要保存或消费数据。
练习
- 给
Stack<T>添加iter(&self),返回内部Vec的迭代器。 - 写一个泛型函数,打印所有实现
Debug的元素。 - 写一个函数接收闭包过滤数字。
- 比较
Fn、FnMut、FnOnce的使用场景。
造轮子任务
把前面的 Stack<T> 做成更像标准库的 API:实现 Default、From<Vec<T>>,添加 into_vec(self) -> Vec<T>,并为不可变遍历提供 iter()。重点是设计调用者舒服的接口。
小结
迭代器、泛型和 trait 是 Rust 抽象能力的核心。它们让你在不牺牲类型安全的前提下写通用代码。好的 API 不只是能用,还要让所有权、借用和错误语义一眼可见。