Skip to content

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 方法,虽然方法的签名有所不同,但使用上差不多。

Released under the MIT License