Appearance
25.2 对比迭代器
生成器本质上跟迭代器是很像的。在 Rust 中,迭代器指的是实现了 std::iter::Iterator
trait 的类型:
rust
pub trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
...
}
这个 trait 最主要的一个方法就是 next
方法。每次调用,它都会返回下一个元素,迭代完成,就返回 None
。使用方法如下:
rust
// 假设 it 是一个迭代器变量
while let Some(item) = it.next() {
do_something(item);
}
next
方法接受的参数是&mut Self
类型。因为它每次调用的时候,都要修改内部的状态,只有这样,下一次调用的时候才会返回不同的内容。如果内部是指针,需要把指针指向容器的下一个元素;如果内部是索引,就需要更新索引的值。
迭代器也可以不指向任何容器,只要它满足 Iterator
trait 这个接口即可。比如 std::ops::Range
这个类型,它代表一个前闭后开的区间,也可以进行迭代,只是每次调用 next
后它代表的区间就变了。
任何一个生成器,总能找到某种办法改写为功能相同的迭代器。还是以前面的 Fibonacci 数列为例,如果改成迭代器的样子,该像下面这样写:
rust
// 方案二
struct Fibonacci {
curr: u64,
next: u64,
}
impl Iterator for Fibonacci {
type Item = u64;
fn next(&mut self) -> Option<u64> {
// 判断是否会溢出
let new_next = self.curr.checked_add(self.next);
if let Some(new_next) = new_next {
// 先更新内部状态,再返回
self.curr = self.next;
self.next = new_next;
Some(self.curr)
} else {
// 加法溢出,停止迭代
None
}
}
}
fn fibonacci() -> Fibonacci {
Fibonacci { curr: 1, next: 1 }
}
fn main() {
let mut it = fibonacci();
while let Some(i) = it.next() {
println!("{}", i);
}
}
这段代码同样也能实现打印 Fibonacci 数列的功能。请读者逐行逐字读一下 next
方法的逻辑,看清楚它是如何记录状态的,理解为什么每次调用 next
方法都会返回不同的值。这个示例在后文还会继续使用。
迭代器模式是一种典型的“拉”模式,它也经常被称为“惰性求值”(lazy evaluation)。生成器在这一点上与迭代器是一样的,也需要使用者调用方法把数据拉出来。它们一个用的是 next
方法,一个用的是 resume
方法,虽然方法的签名有所不同,但使用上差不多。