Expand description
可组合的外部迭代。
如果您发现自己具有某种类型的集合,并且需要对所述集合的元素执行操作,那么您会很快遇到 ‘iterators’。 迭代器在惯用的 Rust 代码中大量使用,所以值得熟悉它们。
在解释更多内容之前,让我们讨论一下该模块的结构:
Organization
该模块主要是按类型来组织的:
- Traits 是核心部分:这些 traits 定义了存在什么样的迭代器,以及您可以用它们做什么。这些 traits 的方法值得投入一些额外的学习时间。
- Functions 提供了一些有用的方法来创建一些基本的迭代器。
- Structs 通常是该模块的 traits 上各种方法的返回类型。通常,您将需要查看创建
struct
的方法,而不是struct
本身。 有关原因的更多详细信息,请参见 实现迭代器。
就是这样! 让我们深入研究迭代器。
Iterator
该模块的核心是 Iterator
trait。Iterator
的核心是这样的:
trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
}
Run迭代器有一个方法 next
,当调用它时,返回一个 Option<Item>
.
只要有元素,调用 next
就会返回 Some(Item)
,一旦它们全部消费完,将返回 None
表示迭代完成。
各个迭代器可能选择恢复迭代,因此再次调用 next
可能会或可能不会最终在某个时候再次开始返回 Some(Item)
(例如,请参见 TryIter
)。
Iterator
的完整定义还包括许多其他方法,但是它们是默认方法,基于 next
构建,因此您可以免费获得它们。
迭代器也是可组合的,通常将它们链接在一起以进行更复杂的处理形式。有关更多详细信息,请参见下面的 适配器 部分。
三种迭代形式
有三种常见的方法可以从集合中创建迭代器:
iter()
,它在&T
上迭代。iter_mut()
,它在&mut T
上迭代。into_iter()
,它在T
上迭代。
在适当的情况下,标准库中的各种内容都可以实现这三个中的一个或多个。
实现迭代器
创建自己的迭代器涉及两个步骤:创建一个 struct
来保存迭代器的状态,然后为该 struct
实现 Iterator
。
这就是为什么此模块中有这么多 struct
的原因:每个迭代器和迭代器适配器都有一个。
让我们创建一个名为 Counter
的迭代器,该迭代器的范围从 1
到 5
:
// 首先,结构体:
/// 从 1 到 5 计数的迭代器
struct Counter {
count: usize,
}
// 我们希望计数从一开始,所以让我们添加一个 new() 方法来提供帮助。
// 这不是严格必要的,但很方便。
// 请注意,我们将 `count` 从零开始,我们将在下面的 `next () ` 实现中看到其原因。
impl Counter {
fn new() -> Counter {
Counter { count: 0 }
}
}
// 然后,我们为 `Counter` 实现 `Iterator`:
impl Iterator for Counter {
// 我们将使用 usize 进行计数
type Item = usize;
// next() 是唯一必需的方法
fn next(&mut self) -> Option<Self::Item> {
// 增加我们的数量。这就是为什么我们从零开始。
self.count += 1;
// 检查我们是否已经完成计数。
if self.count < 6 {
Some(self.count)
} else {
None
}
}
}
// 现在我们可以使用它了!
let mut counter = Counter::new();
assert_eq!(counter.next(), Some(1));
assert_eq!(counter.next(), Some(2));
assert_eq!(counter.next(), Some(3));
assert_eq!(counter.next(), Some(4));
assert_eq!(counter.next(), Some(5));
assert_eq!(counter.next(), None);
Run以这种方式调用 next
将重复进行。Rust 有一个构造,可以在迭代器上调用 next
,直到它到达 None
。让我们接下来讨论。
还要注意,Iterator
提供了内部调用 next
的方法的默认实现,例如 nth
和 fold
。
但是,如果迭代器可以在不调用 next
的情况下更有效地计算它们,则还可以编写方法的自定义实现,例如 nth
和 fold
。
for
循环和 IntoIterator
Rust 的 for
循环语法实际上是迭代器的语法糖。这是 for
的一个基本示例:
let values = vec![1, 2, 3, 4, 5];
for x in values {
println!("{x}");
}
Run这将打印数字 1 到 5,每个数字都在各自的行上。但是您会在这里注意到:我们从未在 vector 上调用任何东西来产生迭代器。是什么给的?
标准库中有一个 trait 用于将某些内容转换为迭代器: IntoIterator
。
这个 trait 具有一个 into_iter
方法,该方法可以将实现 IntoIterator
的类型转换为迭代器。
让我们再次看一下 for
循环,以及编译器将其转换为什么:
let values = vec![1, 2, 3, 4, 5];
for x in values {
println!("{x}");
}
RunRust 将其反糖化为:
let values = vec![1, 2, 3, 4, 5];
{
let result = match IntoIterator::into_iter(values) {
mut iter => loop {
let next;
match iter.next() {
Some(val) => next = val,
None => break,
};
let x = next;
let () = { println!("{x}"); };
},
};
result
}
Run首先,我们在值上调用 into_iter()
。然后,我们在返回的迭代器上进行匹配,一遍又一遍地调用 next
,直到我们看到一个 None
。
到那时,我们 break
退出了循环,我们已经完成了迭代。
这里还有一点微妙之处:标准库包含一个有趣的 IntoIterator
实现:
换句话说,所有 Iterator
都通过返回自身来实现 IntoIterator
。这意味着两件事:
- 如果要编写
Iterator
,则可以将其与for
循环一起使用。 - 如果要创建集合,则为其实现
IntoIterator
将使您的集合可以与for
循环一起使用。
通过引用进行迭代
由于 into_iter()
将 self
作为值,因此使用 for
循环遍历一个集合将消耗该集合。通常,您可能需要迭代一个集合而不使用它。
许多集合提供了在引用上提供迭代器的方法,通常分别称为 iter()
和 iter_mut()
:
let mut values = vec![41];
for x in values.iter_mut() {
*x += 1;
}
for x in values.iter() {
assert_eq!(*x, 42);
}
assert_eq!(values.len(), 1); // `values` 仍然属于此函数。
Run如果集合类型 C
提供 iter()
,则它通常还为 &C
实现 IntoIterator
,而该实现只是调用 iter()
。
同样,提供 iter_mut()
的集合 C
通常通过委派给 iter_mut()
来为 &mut C
实现 IntoIterator
。这样可以方便快捷地实现以下目的:
let mut values = vec![41];
for x in &mut values { // 与 `values.iter_mut()` 相同
*x += 1;
}
for x in &values { // 与 `values.iter()` 相同
assert_eq!(*x, 42);
}
assert_eq!(values.len(), 1);
Run尽管许多集合都提供 iter()
,但并非所有人都提供 iter_mut()
。
例如,如果 key 的哈希发生变化,更改 HashSet<T>
的键可能会使集合处于不一致的状态,因此这个集合仅提供 iter()
。
Adapters
接受一个 Iterator
并返回另一个 Iterator
的函数通常被称为迭代器适配器,因为它们是适配器模式的一种形式。
常见的迭代器适配器包括 map
,take
和 filter
。
有关更多信息,请参见它们的文档。
如果迭代器适配器为 panics,则迭代器将处于未指定 (但内存安全) 状态。 也不能保证此状态在 Rust 的各个版本中都保持不变,因此您应避免依赖 panicked 的迭代器返回的确切值。
Laziness
迭代器 (和迭代器 适配器) 是懒惰的)。这意味着仅仅创建一个迭代器并不会做很多事情。除非您调用 next
,否则什么都不会发生。
当创建仅出于其副作用的迭代器时,这有时会引起混乱。
例如,map
方法在其迭代的每个元素上调用一个闭包:
let v = vec![1, 2, 3, 4, 5];
v.iter().map(|x| println!("{x}"));
Run这将不会打印任何值,因为我们只是创建了一个迭代器,而不是使用它。编译器将警告我们这种行为:
warning: unused result that must be used: iterators are lazy and
do nothing unless consumed
编写 map
的副作用的惯用方式是使用 for
循环或调用 for_each
方法:
let v = vec![1, 2, 3, 4, 5];
v.iter().for_each(|x| println!("{x}"));
// or
for x in &v {
println!("{x}");
}
Run评估迭代器的另一种常见方法是使用 collect
方法来生成新的集合。
Infinity
迭代器不必一定是有限的。例如,开放式范围是一个无限迭代器:
let numbers = 0..;
Run通常使用 take
迭代器适配器将无限迭代器转换为有限迭代器:
let numbers = 0..;
let five_numbers = numbers.take(5);
for number in five_numbers {
println!("{number}");
}
Run这将在各自的行上打印数字 0
至 4
。
请记住,无限迭代器上的方法,即使是可以在有限时间内通过数学方法确定结果的方法,也可能不会终止。
具体来说,通常需要遍历迭代器中每个元素的方法 (如 min
) 对于任何无限迭代器都可能不会成功返回。
let ones = std::iter::repeat(1);
let least = ones.min().unwrap(); // 不好了! 无限循环!
// `ones.min()` 会导致无限循环,所以我们不会达到这一点!
println!("The smallest number one is {least}.");
RunStructs
- ArrayChunksExperimental一次遍历迭代器的
N
个元素的迭代器。 - ByRefSizedExperimental与
Iterator::by_ref
类似,但需要Sized
才能转发泛型。 - IntersperseExperimental在所有元素之间放置分隔符的迭代器适配器。
- IntersperseWithExperimental在所有元素之间放置分隔符的迭代器适配器。
- 链中将两个迭代器链接在一起的迭代器。
- 一个可以克隆底层迭代器元素的迭代器。
- 一个可以复制底层迭代器元素的迭代器。
- 无限重复的迭代器。
- 没有任何结果的迭代器。
- 一个在迭代过程中产生当前计数和元素的迭代器。
- 一个用
predicate
过滤iter
元素的迭代器。 - 一个使用
f
来过滤iter
中的元素和 map 元素的迭代器。 - 将每个元素映射到迭代器的迭代器,并生成生成的迭代器的元素。
- 一个迭代器,它使可迭代化的事物的迭代器中的嵌套层次变得平坦。
- 一个迭代器,每次迭代调用提供的闭包
F: FnMut() -> Option<T>
。 - 一个迭代器,在底层迭代器产生一次
None
之后,永远产生None
。 - 一个迭代器,在产生该迭代器之前,会对每个元素调用带有引用的函数。
- 将
iter
的值与f
映射的迭代器。 - 一个仅在
predicate
返回Some(_)
时才接受元素的迭代器。 - 一个仅产生一次元素的迭代器。
- 通过应用提供的闭包
F: FnOnce() -> A
产生类型为A
的单个元素的迭代器。 - 带有
peek()
的迭代器,该迭代器将可选的引用返回到下一个元素。 - 一个无限重复元素的迭代器。
- 一个迭代器,通过应用提供的闭包
F: FnMut() -> A
,无限地重复A
类型的元素。 - 方向相反的双端迭代器。
- 一个迭代器,用于在迭代另一个迭代器时维持状态。
- 一个跳过
iter
的n
元素的迭代器。 predicate
返回true
时拒绝元素的迭代器。- 一个用于按自定义数量步进迭代器的迭代器。
- 一个新的迭代器,其中每个连续项都是根据前一个进行计算的。
- 一个仅迭代
iter
的前n
迭代的迭代器。 - 一个仅在
predicate
返回true
时才接受元素的迭代器。 - 同时迭代其他两个迭代器的迭代器。
Traits
- StepExperimental具有 successor 和 predecessor 操作概念的对象。
- TrustedLenExperimental一个使用 size_hint 报告准确长度的迭代器。
- TrustedStepExperimental一种支持
Step
的所有不变量的类型。 - 一个能够从两端产生元素的迭代器。
- 知道其确切长度的迭代器。
- 用迭代器的内容扩展集合。
- 从
Iterator
转换。 - 一个迭代器,用完后总是继续产生
None
。 - 转换为
Iterator
。 - 用于处理迭代器的 trait。
- 一个表示可以通过将迭代器的元素相乘来创建类型的 trait。
- 一个表示可以通过对迭代器求和来创建的类型的 trait。
Functions
- from_generatorExperimental创建一个新的迭代器,每次迭代都调用提供的生成器。
- 创建一个不产生任何结果的迭代器。
- 创建一个新的迭代器,每次迭代都调用提供的闭包
F: FnMut() -> Option<T>
。 - 创建一个迭代器,该迭代器只生成一次元素。
- 创建一个迭代器,该迭代器通过调用提供的闭包来懒惰地一次生成一个值。
- 创建一个新的迭代器,该迭代器不断重复单个元素。
- 创建一个新的迭代器,通过应用提供的闭包,转发器
F: FnMut() -> A
,无限地重复A
类型的元素。 - 创建一个新的迭代器,在该迭代器的基础上,每个连续项都根据前一个进行计算。
- 将参数转换为迭代器并压缩它们。