Iterator

Iter和for循环

Rust中调用迭代器的接口就是Iterator,通常情况之下看到的for循环 就是这种迭代器的语法糖。
这种for循环使用into_iter()在T上迭代。

1
2
3
4
trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
}
  • 通常情况之下,使用for循环会消耗这个集合,因为使用了into_iter()方法,但是实际上可以使用其他方法,如:
1
2
3
4
5
6
7
8
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` 仍然属于此函数。

或者下面的:

1
2
3
4
5
6
7
8
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);

二者相同,因为如果集合类型 C 提供 iter(),则它通常还为 &C 实现 IntoIterator,而该实现只是调用 iter()。 同样,提供 iter_mut() 的集合 C 通常通过委派给 iter_mut() 来为 &mut C 实现 IntoIterator。

Adapters

  • 接受Iterator并且返回另外一个Iterator的函数叫做迭代器适配器。常见的方法有map、take、filter。
  • map()

map通常是是实现一个闭包,这个闭包是FnMut(实例可以重复调用,并且可以改变值)类型的,它的作用是:
将一种A元素转换为想要的B元素。
map()和for循环比较像,但是如果已经使用了其他类型的迭代器,最好使用map();而在使用某种循环的副作用,使用for

1
2
3
4
5
6
7
8
let a = [1, 2, 3];

let mut iter = a.iter().map(|x| 2 * x);

assert_eq!(iter.next(), Some(2));
assert_eq!(iter.next(), Some(4));
assert_eq!(iter.next(), Some(6));
assert_eq!(iter.next(), None);

关于副作用(这段代码不会运行

1
2
3
4
5
6
7
8
9
// 不要这样做:
(0..5).map(|x| println!("{}", x));

// 它甚至不会执行,因为它很懒。Rust 会就此警告您。

// 而是用于:
for x in 0..5 {
println!("{}", x);
}
  • filter

过滤值,同样是创建一个闭包(这个闭包会获取引用)

1
2
3
4
5
let a = [0i32, 1, 2];
let mut iter = a.iter().filter(|x| x.is_positive());
assert_eq!(iter.next(), Some(&1));
assert_eq!(iter.next(), Some(&2));
assert_eq!(iter.next(), None);

在使用过程中会遇到多个迭代器使用的时候,这个时候需要双引用,这个时候filter就需要进行改进:

1
2
3
4
5
6
let a = [0, 1, 2];

let mut iter = a.iter().filter(|x| **x > 1); // 需要两个 *s!

assert_eq!(iter.next(), Some(&2));
assert_eq!(iter.next(), None);

可以使用解构去掉一个

1
2
3
4
5
6
let a = [0, 1, 2];

let mut iter = a.iter().filter(|&x| *x > 1); // & 和 *

assert_eq!(iter.next(), Some(&2));
assert_eq!(iter.next(), None);

或者两个

1
2
3
4
5
6
let a = [0, 1, 2];

let mut iter = a.iter().filter(|&&x| x > 1); // 两个 &s

assert_eq!(iter.next(), Some(&2));
assert_eq!(iter.next(), None);
  • take

take创建一个迭代器,在迭代过程之中提前结束

1
2
3
4
5
6
7
let a = [1, 2, 3];

let mut iter = a.iter().take(2);

assert_eq!(iter.next(), Some(&1));
assert_eq!(iter.next(), Some(&2));
assert_eq!(iter.next(), None);

take() 通常与无限迭代器一起使用,以使其成为有限的:

1
2
3
4
5
6
let mut iter = (0..).take(3);

assert_eq!(iter.next(), Some(0));
assert_eq!(iter.next(), Some(1));
assert_eq!(iter.next(), Some(2));
assert_eq!(iter.next(), None);

如果少于 n 个元素可用,take 会将自身限制为底层迭代器的大小:

1
2
3
4
5
let v = vec![1, 2];
let mut iter = v.into_iter().take(5);
assert_eq!(iter.next(), Some(1));
assert_eq!(iter.next(), Some(2));
assert_eq!(iter.next(), None);
  • enumerate

enumerate会创建一个迭代器,这个迭代器给出当前的迭代次数以及下一个值,返回的迭代器索引是(i,val),i为当前索引,val是迭代器返回的值。

1
2
3
4
5
6
7
8
let a = ['a', 'b', 'c'];

let mut iter = a.iter().enumerate();

assert_eq!(iter.next(), Some((0, &'a')));//因为是迭代器,所以这里是用了解引用
assert_eq!(iter.next(), Some((1, &'b')));
assert_eq!(iter.next(), Some((2, &'c')));
assert_eq!(iter.next(), None);
  • zip

zip会将两个迭代器压缩为单个迭代器,这个迭代器是成对的。

如果其中一个迭代器返回None,则整个迭代器返回None

1
2
3
4
5
6
7
8
9
let a1 = [1, 2, 3];
let a2 = [4, 5, 6];

let mut iter = a1.iter().zip(a2.iter());

assert_eq!(iter.next(), Some((&1, &4)));
assert_eq!(iter.next(), Some((&2, &5)));
assert_eq!(iter.next(), Some((&3, &6)));
assert_eq!(iter.next(), None);
  • collect

collect会将可迭代的东西收集成为一个集合,基本方法是使用iter,最后使用collect

1
2
3
4
5
6
7
let a = [1, 2, 3];

let doubled: Vec<i32> = a.iter()
.map(|&x| x * 2)
.collect();

assert_eq!(vec![2, 4, 6], doubled);

请注意,我们需要在左侧使用 : Vec。这是因为我们可以代替收集到例如 VecDeque 中:

1
2
3
4
5
6
7
8
9
use std::collections::VecDeque;

let a = [1, 2, 3];

let doubled: VecDeque<i32> = a.iter().map(|&x| x * 2).collect();

assert_eq!(2, doubled[0]);
assert_eq!(4, doubled[1]);
assert_eq!(6, doubled[2]);

使用 ‘turbofish’ 而不是注解 doubled:

1
2
3
4
5
let a = [1, 2, 3];

let doubled = a.iter().map(|x| x * 2).collect::<Vec<i32>>();

assert_eq!(vec![2, 4, 6], doubled);

因为 collect() 只关心您要收集的内容,所以您仍然可以将局部类型提示 _ 与 turbfish 一起使用:

1
2
3
4
5
let a = [1, 2, 3];

let doubled = a.iter().map(|x| x * 2).collect::<Vec<_>>();

assert_eq!(vec![2, 4, 6], doubled);

使用 collect() 生成 String:

1
2
3
4
5
6
7
8
let chars = ['g', 'd', 'k', 'k', 'n'];

let hello: String = chars.iter()
.map(|&x| x as u8)
.map(|x| (x + 1) as char)
.collect();

assert_eq!("hello", hello);

如果您有 Result<T, E>,您可以使用 collect() 来查看它们是否失败:

1
2
3
4
5
6
7
8
9
10
11
12
13
let results = [Ok(1), Err("nope"), Ok(3), Err("bad")];

let result: Result<Vec<_>, &str> = results.iter().cloned().collect();

// 给我们第一个错误
assert_eq!(Err("nope"), result);

let results = [Ok(1), Ok(3)];

let result: Result<Vec<_>, &str> = results.iter().cloned().collect();

// 给我们答案列表
assert_eq!(Ok(vec![1, 3]), result);

迭代器的惰性

通常情况之下迭代器如果作用的结果是副作用的时候,尽量使用for,因为编译器会警告,并且这不会运行。

1
2
3
4
5
6
7
8
9

let v = vec![1, 2, 3, 4, 5];

v.iter().for_each(|x| println!("{}", x));
// or
for x in &v {
println!("{}", x);
}

Infinity

开放式迭代器是无限迭代器。
可以使用take方法更改。

1
2
3
4
5
6
7

let numbers = 0..;
let five_numbers = numbers.take(5);

for number in five_numbers {
println!("{}", number);
}

迭代器的三种形式

  • iter():在&T上迭代
    iter_mut():在&mut上迭代
    into_iter():在T上迭代
    更多内容可以查看std::iter