Module std::iter 1.0.0[−][src]
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
构建,因此您可以免费获得它们。
迭代器也是可组合的,通常将它们链接在一起以进行更复杂的处理形式。有关更多详细信息,请参见下面的 Adapters 部分。
三种迭代形式
有三种常见的方法可以从集合中创建迭代器:
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); }Run
Rust 将其反糖化为:
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
实现:
impl<I: Iterator> IntoIterator for IRun
换句话说,所有 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()
。
例如,如果键的哈希值发生更改,则对 HashSet<T>
或 HashMap<K, V>
的键进行更改可能会使该集合处于不一致状态,因此这些集合仅提供 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);Run
Structs
Intersperse | Experimental 在所有元素之间放置分隔符的迭代器适配器。 |
IntersperseWith | Experimental 在所有元素之间放置分隔符的迭代器适配器。 |
MapWhile | Experimental 一个仅在 |
Chain | 链中将两个迭代器链接在一起的迭代器。 |
Cloned | 一个迭代器,可克隆基础迭代器的元素。 |
Copied | 复制基础迭代器元素的迭代器。 |
Cycle | 无限重复的迭代器。 |
Empty | 没有任何结果的迭代器。 |
Enumerate | 一个在迭代过程中产生当前计数和元素的迭代器。 |
Filter | 一个用 |
FilterMap | 一个使用 |
FlatMap | Z0 将每个元素映射到一个迭代器,并产生所生成的迭代器的元素的迭代器。 |
Flatten | 一个迭代器,它使可迭代化的事物的迭代器中的嵌套层次变得平坦。 |
FromFn | 一个迭代器,每次迭代调用提供的闭包 |
Fuse | 在基础迭代器一次生成 |
Inspect | 一个迭代器,在产生该迭代器之前,会对每个元素调用带有引用的函数。 |
Map | 将 |
Once | 一个仅产生一次元素的迭代器。 |
OnceWith | 通过应用提供的闭包 |
Peekable | 带有 |
Repeat | 一个无限重复元素的迭代器。 |
RepeatWith | 一个迭代器,通过应用提供的闭包 |
Rev | 方向相反的双端迭代器。 |
Scan | 一个迭代器,用于在迭代另一个迭代器时维持状态。 |
Skip | 一个跳过 |
SkipWhile |
|
StepBy | 一个用于按自定义数量步进迭代器的迭代器。 |
Successors | 一个新的迭代器,其中每个连续项都是根据前一个进行计算的。 |
Take | 一个仅迭代 |
TakeWhile | 一个仅在 |
Zip | 同时迭代其他两个迭代器的迭代器。 |
Traits
Step | Experimental 具有 successor 和 predecessor 操作概念的对象。 |
TrustedLen | Experimental 一个使用 size_hint 报告准确长度的迭代器。 |
TrustedStep | Experimental 一种支持 |
DoubleEndedIterator | 一个能够从两端产生元素的迭代器。 |
ExactSizeIterator | 知道其确切长度的迭代器。 |
Extend | 用迭代器的内容扩展集合。 |
FromIterator | 从 |
FusedIterator | 一个迭代器,用完后总是继续产生 |
IntoIterator | 转换为 |
Iterator | 用于处理迭代器的接口。 |
Product | Trait 表示可以通过乘以迭代器的元素来创建的类型。 |
Sum | Trait 表示可以通过汇总迭代器创建的类型。 |
Functions
zip | Experimental 将参数转换为迭代器并压缩它们。 |
empty | 创建一个不产生任何结果的迭代器。 |
from_fn | 创建一个新的迭代器,每次迭代都调用提供的闭包 |
once | 创建一个迭代器,该迭代器只生成一次元素。 |
once_with | 创建一个迭代器,该迭代器通过调用提供的闭包来懒惰地一次生成一个值。 |
repeat | 创建一个新的迭代器,该迭代器不断重复单个元素。 |
repeat_with | 创建一个新的迭代器,方法是应用提供的闭包,转发器,无限地重复 |
successors | 创建一个新的迭代器,在该迭代器的基础上,每个连续项都根据前一个进行计算。 |