rust 自定义迭代器的实现方法
作者:码事漫谈
1. 什么是迭代器?🧠
简单来说,迭代器就是一个“知道如何获取下一个元素”的东西。
它是一种设计模式,允许你遍历一个序列(比如数组、列表或你自定义的任何东西),而不需要关心序列内部是怎么存储的。
在 Rust 中,你最常见的迭代器用法就是 for 循环:r` 循环:
let numbers = vec![1, 2, 3];
// 这里的 `numbers.iter()` 就创建了一个迭代器
for num in numbers.iter() {
println!("Got: {}", num);
}
for 循环就是不断地问这个迭代器:“嘿,还有下一个吗?有的话请给我。” 直到迭代器回答:“抱歉,没有了。”
我们的目标就是学会如何创造这种“东西”。
2. 核心揭秘:IteratorTrait
在 Rust 中,“迭代器”并不是一个具体的类型,而是**任何了 Iterator Trait 的类型**。
这个 Trait (特质) 的定义简化后是这样的:
pub trait Iterator {
// 1. 关联类型:告诉 Rust 你迭代的“东西”是什么类型
type Item;
// 2. 核心方法:获取下一个元素
// 这是你唯一必须实现的方法!
fn next(&mut self) -> Option<Self::Item>;
// --- 下面还有很多其他方法 (map, filter, sum...) ---
// 但它们都有默认实现,你暂时不用管!
}
是不是看起来很简单?我们来拆解一下你必须关心的两个部分:
type Item;
这是一个关联类型。你只需要告诉 Rust:“我这个迭代器,每次‘吐’出来的元素是 i32 类型”或者“是 String 类型”。
例如:type Item = u32; 或 type Item = &String;
fn next(&mut self) -> Option<Self::Item>
这就是魔法发生的地方!🌟
&mut self:为什么是 &mut (可变借用)?因为迭代器需要**状态**。比如,一个计数器需要知道“我当前数到几了”,每次调用 next 之后,这个状态就要改变(比如 +1)。
Option<Self::Item>:这是迭代器设计的精髓!
- **`Some(ue)**:如果序列中还有下一个元素,就返回 Some(那个元素)`。
- None:如果序列已经结束了,就返回 None。for 循环看到 None 就会自动停止。
3. 实践一:你的第一个迭代器 (简单的计数器) 🌍
我们来写一个最简单的迭代器:一个从 1 数到 5 的计数器。
第 1 步:定义结构体 (保存状态)
迭代器需要“记忆”,所以我们需要一个结构体来保存它的“状态”。对于计数器,我们需要知道“当前数到几了” (current) 和“什么时候停” (max)。
// 我们的计数器结构体
struct Counter {
current: u32,
max: u32,
}
// 顺便给它一个 "构造函数" (new)
impl Counter {
fn new(max: u32) -> Counter {
Counter { current: 1, max } // 我们从 1 开始数
}
}
第 2 步:实现IteratorTrait
现在,我们来告诉 Rust 如何让 Counter 变成一个迭代器。
impl Iterator for Counter {
// 1. 告诉 Rust 我们迭代的是 u32
type Item = u32;
// 2. 实现核心逻辑!
fn next(&mut self) -> Option<Self::Item> {
if self.current <= self.max {
// 只要当前值 <= 5
// 准备好要返回的当前值
let val_to_return = self.current;
// 更新状态:让 current + 1,为下一次做准备
self.current += 1;
// 把值用 Some() 包裹起来返回
Some(val_to_return)
} else {
// 如果 current 已经 > max (比如到了 6)
// 迭代结束!返回 None
None
}
}
}
第 3 步:使用它!
恭喜你!你已经写好了一个完整的迭代器!🎉 让我们用用看:
fn main() {
let counter = Counter::new(5); // 创建一个 1 到 5 的计数器
// `for` 循环现在可以识别我们的 Counter 了!
println!("Running for loop:");
for number in counter {
println!("{}", number);
}
// 注意:`for` 循环会“消耗掉”迭代器。
// 如果想再用一次,需要重新创建:
let counter2 = Counter::new(3);
// 你也可以手动调用 next() 看看发生了什么
println!("\nManual next() calls:");
let mut counter3 = Counter::new(2); // 必须是 mut,因为 next() 需要 &mut self
println!("{:?}", counter3.next()); // Some(1)
println!("{:?}", counter3.next()); // Some(2)
println!("{:?}", counter3.next()); // None (迭代结束)
println!("{:?}", counter3.next()); // None (之后永远是 None)
}
输出:
Running for loop: 1 2 3 4 5 Manual next() calls: Some(1) Some(2) None None
你已经掌握了 80% 的精髓了!太棒了!🥳
4. 实践二:让自定义结构体“可迭代” 🌟
在实践一中,Counter 本身就是迭代器。但更常见的情况是:你有一个集合(比如 `Myook),你想**为它创建一个迭代器**(比如 BookPageIterator`)。
就像 Vec (集合) 和 VecIter (它的迭代器) 的关系一样。
我们希望实现这样的效果:
let my_list = MyList::new();
for item in &my_list { // 注意这里是 &my_list
// ...
}
要实现这个,我们需要两个 Trait:Iterator (老朋友) 和 IntoIterator (新朋友)。
IntoIterator Trait 就像一个“转换器”,它告诉 for 循环:“嘿,我知道如何把我(&MyList)转换成一个真正的迭代器!”
第 1 步:定义集合和它的迭代器结构体
// 我们的集合
struct MyList {
items: Vec<String>,
}
impl MyList {
fn new() -> Self {
Self {
items: vec![
"Rust".to_string(),
"is".to_string(),
"Awesome".to_string(),
],
}
}
}
// ------------------------------------
// 专门为 MyList 服务的迭代器结构体
// 它需要“借用” MyList 的数据
// 'a 是生命周期,表示它借用的数据至少和 'a 活得一样久
struct MyListIter<'a> {
list: &'a MyList, // 持有对 MyList 的引用
index: usize, // 跟踪迭代到第几个了
}
第 2 步:为 `MyListIter 实现Iterator
这和 Counter 的例子几乎一样,只是现在我们是从 Vec 中取数据。
// 'a 也要在这里声明
impl<'a> Iterator for MyListIter<'a> {
// 这一次,我们迭代的是对 String 的引用
type Item = &'a String;
fn next(&mut self) -> Option<Self::Item> {
if self.index < self.list.items.len() {
// 还有元素
let item = &self.list.items[self.index];
self.index += 1;
Some(item)
} else {
// 没元素了
None
}
}
}
第 3 步:关键!为MyList实现IntoIterator
这是连接 for 循环和 MyListIter 的“胶水”。我们希望 for item in &my_list 能工作,所以我们要为 &MyList 实现 IntoIterator。
// 为 &MyList (对 MyList 的不可变引用) 实现 IntoIterator
impl<'a> IntoIterator for &'a MyList {
// 迭代项还是 &String
type Item = &'a String;
// 告诉 for 循环:你调用 into_iter() 时,
// 我会返回一个 MyListIter<'a> 实例
type IntoIter = MyListIter<'a>;
// `for` 循环会自动调用这个方法!
// 这里的 self 就是 &'a MyList
fn into_iter(self) -> Self::IntoIter {
// 创建我们刚才定义的迭代器实例
MyListIter {
list: self, // self 就是 &MyList
index: 0, // 从 0 开始
}
}
}
第 4 步:见证奇迹!
fn main() {
let my_list = MyList::new();
// 感谢 IntoIterator,这行代码现在可以完美工作了!
// 1. `for` 循环看到 &my_list
// 2. 它调用 (&my_list).into_iter()
// 3. 我们的代码返回了一个 MyListIter
// 4. `for` 循环不断调用 MyListIter.next()
for item in &my_list {
println!("Item: {}", item);
}
}
输出:
Item: Rust
Item: is
Item: Awesome
你做到了!这已经是 Rust 中非常地道的迭代器实现方式了!👍
5. 你免费获得的“超能力” 😎
最爽的部分来了!
当你辛辛苦苦地实现了 Iterator Trait(哪怕只写了 next() 方法),Rust 编译器会免费赠送给你一大堆超级好用的“迭代器适配器” (Iterator Adapters)!
比如 .map(), .filter(), .zip(), .sum(), .collect()… 全都能用了!
看看我们刚才的 Counter:
let sum: u32 = Counter::new(5) // 我们的迭代器 (1, 2, 3, 4, 5)
.zip(Counter::new(5).skip(1)) // ( (1,2), (2,3), (3,4), (4,5) )
.map(|(a, b)| a * b) // ( 2, 6, 12, 20 )
.filter(|x| *x > 10) // ( 12, 20 )
.sum(); // 12 + 20 = 32
println!("The complex sum is: {}", sum); // 32
我们只写了 next(),但 zip, skip, map, filter, sum 都能在我们的 Counter 上使用!这就是 Rust Trait 和迭代器模式的强大之处!
6. 总结 & 下一步
我们来回顾一下关键点:
- 迭代器是任何实现了 Iterator Trait 的东西。
- Iterator Trait 的核心是 type Item; (迭代什么) 和 `fn next(&mut self) -> OptionSelf::Item 你需要一个 struct 来保存迭代的状态 (比如 current 索引)。
- `next)方法通过返回Some(value)来提供值,通过返回None` 来停止迭代。
- 要让你自己的集合(如 MyList)支持 for item in &collection,你需要为 &collection 实现 IntoIterator Trait,让它返回你自定义的迭代器(如 MyListIter)。
- 一旦实现了 Iterator,你就免费获得了所有适配器 (map, `filter …)。
到此这篇关于rust 自定义迭代器的实现方法的文章就介绍到这了,更多相关rust 自定义迭代器内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
