Module std::iter1.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 的迭代器,该迭代器的范围从 15:

// 首先,结构体:

/// 从 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 的方法的默认实现,例如 nthfold。 但是,如果迭代器可以在不调用 next 的情况下更有效地计算它们,则还可以编写方法的自定义实现,例如 nthfold

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 I
Run

换句话说,所有 Iterator 都通过返回自身来实现 IntoIterator。这意味着两件事:

  1. 如果要编写 Iterator,则可以将其与 for 循环一起使用。
  2. 如果要创建集合,则为其实现 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 的函数通常被称为迭代器适配器,因为它们是适配器模式的一种形式。

常见的迭代器适配器包括 maptakefilter。 有关更多信息,请参见其文档。

如果迭代器适配器为 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

这将在各自的行上打印数字 04

请记住,无限迭代器上的方法,即使可以在有限时间内数学确定结果的方法,也可能不会终止。 具体来说,通常需要遍历迭代器中每个元素的方法 (如 min) 对于任何无限迭代器都可能不会成功返回。

let ones = std::iter::repeat(1);
let least = ones.min().unwrap(); // 不好了! 无限循环!
// `ones.min()` 导致无限循环,所以我们不会达到这一点!
println!("The smallest number one is {}.", least);
Run

Structs

IntersperseExperimental

在所有元素之间放置分隔符的迭代器适配器。

IntersperseWithExperimental

在所有元素之间放置分隔符的迭代器适配器。

MapWhileExperimental

一个仅在 predicate 返回 Some(_) 时才接受元素的迭代器。

Chain

链中将两个迭代器链接在一起的迭代器。

Cloned

一个迭代器,可克隆基础迭代器的元素。

Copied

复制基础迭代器元素的迭代器。

Cycle

无限重复的迭代器。

Empty

没有任何结果的迭代器。

Enumerate

一个在迭代过程中产生当前计数和元素的迭代器。

Filter

一个用 predicate 过滤 iter 元素的迭代器。

FilterMap

一个使用 f 来过滤 iter 中的元素和 map 元素的迭代器。

FlatMap

Z0 将每个元素映射到一个迭代器,并产生所生成的迭代器的元素的迭代器。

Flatten

一个迭代器,它使可迭代化的事物的迭代器中的嵌套层次变得平坦。

FromFn

一个迭代器,每次迭代调用提供的闭包 F: FnMut() -> Option<T>

Fuse

在基础迭代器一次生成 None 之后,将永远生成 None 的迭代器。

Inspect

一个迭代器,在产生该迭代器之前,会对每个元素调用带有引用的函数。

Map

iter 的值与 f 映射的迭代器。

Once

一个仅产生一次元素的迭代器。

OnceWith

通过应用提供的闭包 F: FnOnce() -> A 产生类型为 A 的单个元素的迭代器。

Peekable

带有 peek() 的迭代器,该迭代器将可选的引用返回到下一个元素。

Repeat

一个无限重复元素的迭代器。

RepeatWith

一个迭代器,通过应用提供的闭包 F: FnMut() -> A,无限地重复 A 类型的元素。

Rev

方向相反的双端迭代器。

Scan

一个迭代器,用于在迭代另一个迭代器时维持状态。

Skip

一个跳过 itern 元素的迭代器。

SkipWhile

predicate 返回 true 时拒绝元素的迭代器。

StepBy

一个用于按自定义数量步进迭代器的迭代器。

Successors

一个新的迭代器,其中每个连续项都是根据前一个进行计算的。

Take

一个仅迭代 iter 的前 n 迭代的迭代器。

TakeWhile

一个仅在 predicate 返回 true 时才接受元素的迭代器。

Zip

同时迭代其他两个迭代器的迭代器。

Traits

StepExperimental

具有 successorpredecessor 操作概念的对象。

TrustedLenExperimental

一个使用 size_hint 报告准确长度的迭代器。

TrustedStepExperimental

一种支持 Step 的所有不可变变量的类型。

DoubleEndedIterator

一个能够从两端产生元素的迭代器。

ExactSizeIterator

知道其确切长度的迭代器。

Extend

用迭代器的内容扩展集合。

FromIterator

Iterator 转换。

FusedIterator

一个迭代器,用完后总是继续产生 None

IntoIterator

转换为 Iterator

Iterator

用于处理迭代器的接口。

Product

Trait 表示可以通过乘以迭代器的元素来创建的类型。

Sum

Trait 表示可以通过汇总迭代器创建的类型。

Functions

zipExperimental

将参数转换为迭代器并压缩它们。

empty

创建一个不产生任何结果的迭代器。

from_fn

创建一个新的迭代器,每次迭代都调用提供的闭包 F: FnMut() -> Option<T>

once

创建一个迭代器,该迭代器只生成一次元素。

once_with

创建一个迭代器,该迭代器通过调用提供的闭包来懒惰地一次生成一个值。

repeat

创建一个新的迭代器,该迭代器不断重复单个元素。

repeat_with

创建一个新的迭代器,方法是应用提供的闭包,转发器,无限地重复 A 类型的元素, F: FnMut() -> A.

successors

创建一个新的迭代器,在该迭代器的基础上,每个连续项都根据前一个进行计算。