迭代 (Iteration)

最早的练习里,你已经了解到 Rust 允许你用 for 循环遍历集合。 当时我们看的是范围(例如 0..5),但同样的规则也适用于像数组和向量这样的集合。

// 适用于 `Vec`
let v = vec![1, 2, 3];
for n in v {
    println!("{}", n);
}

// 也适用于数组
let a: [u32; 3] = [1, 2, 3];
for n in a {
    println!("{}", n);
}

是时候了解它在底层是如何工作的了。

for 的脱糖 (for desugaring)

每当你在 Rust 里写 for 循环时,编译器会把它 脱糖 (desugar) 成下面这样的代码:

let mut iter = IntoIterator::into_iter(v);
loop {
    match iter.next() {
        Some(n) => {
            println!("{}", n);
        }
        None => break,
    }
}

loop 是除 forwhile 之外的另一种循环构造。
loop 块会无限运行,除非你显式 break

Iterator 特质 (Iterator trait)

前面代码片段中的 next 方法来自 Iterator 特质。 Iterator 特质定义在 Rust 标准库中,为能够产生一系列值的类型提供共享接口:

trait Iterator {
    type Item;
    fn next(&mut self) -> Option<Self::Item>;
}

Item 关联类型 (associated type) 指定了迭代器产生的值的类型。

next 返回序列中的下一个值。
如果有值可返回则返回 Some(value),没有则返回 None

注意:迭代器返回 None 不保证它已经耗尽。这只在迭代器实现了(更严格的)FusedIterator 特质时才能保证。

IntoIterator 特质 (IntoIterator trait)

并不是所有类型都实现了 Iterator,但很多类型可以转换成实现了 Iterator 的类型。
这就是 IntoIterator 特质的用武之地:

trait IntoIterator {
    type Item;
    type IntoIter: Iterator<Item = Self::Item>;
    fn into_iter(self) -> Self::IntoIter;
}

into_iter 方法消费原值,返回它的元素上的迭代器。
一个类型只能有一个 IntoIterator 实现:for 该脱糖成什么不能有歧义。

一个细节:每个实现了 Iterator 的类型也自动实现了 IntoIterator。 它们的 into_iter 直接返回自身!

边界检查 (Bounds checks)

迭代有一个不错的副作用:按设计你不会越界。
这允许 Rust 在生成的机器码中去掉边界检查,使迭代更快。

换句话说,

let v = vec![1, 2, 3];
for n in v {
    println!("{}", n);
}

通常比

let v = vec![1, 2, 3];
for i in 0..v.len() {
    println!("{}", v[i]);
}

更快。

这条规则有例外:编译器有时能证明你没有越界,即使是手动索引也能去掉边界检查。但总的来说,能用迭代就别用索引。

原文链接:英文原文