Rust Iterator
Table of Contents
1. Rust 迭代器
Rust 中使用迭代器可以方便地处理序列数据。
迭代器需要实现 Iterator trait:
trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
}
从上定义可知,迭代器只有一个必须实现的方法,即 next() 。
Rust 中的 Range 就实现了 Iterator trait,下面是它的例子:
fn main() {
let mut x = 0..3;
assert_eq!(x.next(), Some(0));
assert_eq!(x.next(), Some(1));
assert_eq!(x.next(), Some(2));
assert_eq!(x.next(), None);
}
又如, String 和 &str 的 lines() 方法返回的也是迭代器,下面是它的例子:
fn main() {
let text = "line 1\nline 2";
let mut x = text.lines(); // lines() 返回一个迭代器
assert_eq!(x.next(), Some("line 1"));
assert_eq!(x.next(), Some("line 2"));
assert_eq!(x.next(), None);
}
1.1. 3 种形式的迭代器(iter/iter_mut/into_iter)
一般地,从一个 Collection 上的可以得到 3 种形式的迭代器(有些 Collection 可能只实现了其中 1 种或者 2 种):
iter(), which iterates over&T.iter_mut(), which iterates over&mut T.into_iter(), which iterates overT.
下面依次介绍它们。
iter() 最为常用,下面是它的例子:
fn main() {
let x = [1, 2, 3];
for element in x.iter() { // 使用 iter() 得到迭代器
println!("{}", element); // element 的类型是 &i32
}
}
通过 iter_mut() 可以修改 Collection 中数据,下面是它的例子:
fn main() {
let mut x = [1, 2, 3];
for element in x.iter_mut() { // 使用 iter_mut() 得到可修改迭代器
*element += 1; // 每个元素都加 1,element 的类型是 &mut i32
}
println!("{:?}", x); // [2, 3, 4]
}
使用 into_iter() 得到的迭代器在遍历时会消耗掉 Collection 中元素,下面是它的例子:
fn main() {
#[derive(Debug)]
struct Number(i32);
let x = vec![Number(1), Number(2), Number(3)];
for element in x.into_iter() { // Number 未实现 Copy trait,所有权会转移到 element 上
println!("{:?}", element) // element 的类型是 Number,如果上一行使用 x.iter(),则 element 的类型是 &Number
}
// println!("{:?}", x); // 这行会报错。x 中所有元素都已经被消耗了,不再可访问。如果前面改为 x.iter() 则还可访问
}
上面场景中, into_iter() 可以直接省略,也就是说上面代码等同于:
fn main() {
#[derive(Debug)]
struct Number(i32);
let x = vec![Number(1), Number(2), Number(3)];
for element in x { // for ... in x 等同于 for ... in x.into_iter()
println!("{:?}", element)
}
// println!("{:?}", x);
}
1.2. for 循环是迭代器的语法糖
Rust 中 for 循环是迭代器的语法糖,比如下面 for 循环:
let values = vec![1, 2, 3, 4, 5];
for x in values {
println!("{}", x);
}
相当于:
let values = vec![1, 2, 3, 4, 5];
{
let result = match IntoIterator::into_iter(values) {
mut iter => loop {
let next;
match iter.next() { // 调用迭代器的 next() 方法
Some(val) => next = val,
None => break,
};
let x = next;
let () = { println!("{}", x); };
},
};
result
}
2. 实现迭代器
下面是为 Counter 实现迭代器的例子,从 1 到 5 计数:
// First, the struct:
struct Counter {
count: usize,
}
// we want our count to start at one, so let's add a new() method to help.
// This isn't strictly necessary, but is convenient. Note that we start
// `count` at zero, we'll see why in `next()`'s implementation below.
impl Counter {
fn new() -> Counter {
Counter { count: 0 }
}
}
// Then, we implement `Iterator` for our `Counter`:
impl Iterator for Counter {
// we will be counting with usize
type Item = usize;
// next() is the only required method
fn next(&mut self) -> Option<Self::Item> {
// Increment our count. This is why we started at zero.
self.count += 1;
// Check to see if we've finished counting or not.
if self.count < 6 {
Some(self.count)
} else {
None
}
}
}
fn main() {
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);
}
3. 迭代器是惰性的
迭代器是惰性的,创建迭代器时并不会对 Collection 中元素进行遍历,直到遇到迭代器消费者(它会调用迭代器的 next() 方法)才会触发遍历。 比如:
fn main() {
let text = " line 1 \n line 2 ";
let trimmed = text.lines() // lines() 返回一个迭代器,现在还不会开始遍历 text
.map(str::trim) // map 返回另一个迭代器,现在还不会开始遍历 text
.collect::<Vec<&str>>(); // collect() 是迭代器消费者,这时才开始遍历 text
println!("{:?}" , trimmed); // ["line 1", "line 2"]
}
4. 迭代器适配器(一个迭代器转换为另一个迭代器)
4.1. map
在节 3 中介绍过 map,它可以对 Collection 中元素按规则进行映射,下面再看个 map 的使用例子:
fn main() { // iter().map 形式一
let a: Vec<i32> = vec![1, -2, -3, 4, 5];
let b = a
.iter()
.map(|x: &i32| (*x) * 3); // x 类型为 &i32,这是因为 iter() 在类型 &T 上遍历,(*x) * 3 可简化,后面会介绍
let c: Vec<i32> = b.collect();
println!("{:?}", c) // [3, -6, -9, 12, 15]
}
上面例子中,map 接收 closure,这个 closure 的参数类型是 &i32 ,为什么是这样呢?这是因为 iter() 会在 &T 上遍历,详情可参考节 1.1 。
如果我们把上面例子中的 iter() 换为 into_iter() ,则 closure 的参数类型就是 i32 了:
fn main() {
let a: Vec<i32> = vec![1, -2, -3, 4, 5];
let b = a
.into_iter()
.map(|x: i32| x * 3); // x 类型为 i32,这是因为 into_iter() 在类型 T 上遍历
let c: Vec<i32> = b.collect();
println!("{:?}", c) // [3, -6, -9, 12, 15]
}
需要说明的是,由于类型 &i32 “重载”了乘法操作符 Mul ,可以直接把 &i32 和 i32 相乘:
fn main() { // iter().map 形式二
let a: Vec<i32> = vec![1, -2, -3, 4, 5];
let b = a
.iter()
.map(|x: &i32| x * 3); // x 类型为 &i32,也可以直接把它和数字 3 相乘
let c: Vec<i32> = b.collect();
println!("{:?}", c) // [3, -6, -9, 12, 15]
}
fn main() { // iter().map 形式三
let a: Vec<i32> = vec![1, -2, -3, 4, 5];
let b = a
.iter()
.map(|&x| x * 3); // closure 参数类型为 &i32,如果参数写为 &x,则 x 类型就是 i32
let c: Vec<i32> = b.collect();
println!("{:?}", c) // [3, -6, -9, 12, 15]
}
4.2. filter
filter 可以对 Collection 中元素按规则进行过滤,下面是 filter 的使用例子:
fn main() {
let a: Vec<i32> = vec![1, -2, -3, 4, 5];
let b = a
.iter() // iter() 会在 &T 上遍历
.filter(|x: &&i32| **x > 0); // x 的类型为 &&i32,一个 & 源于 iter(),另一个 & 源于 filter
let c: Vec<&i32> = b.collect();
println!("{:?}", c) // [1, 4, 5]
}
这里需要注意一下,上面例子中 filter 参数是 closure,closure 的参数 x 的类型为 &&i32 ,这导致 closure 内部使用 **x 才能得到原始的值。
我们知道,使用 iter() 时,会在 &T 上遍历,之所以还多一个 & 的原因是 filter 本身就接收一个引用,下面是 filter 的签名:
fn filter<P>(self, predicate: P) -> Filter<Self, P>
where
Self: Sized,
P: FnMut(&Self::Item) -> bool
// ^
// |
// 这里有个 &
如果我们把上面例子中的 iter() 换为 into_iter() ,则 closure 的参数类型就是 &i32 了:
fn main() {
let a: Vec<i32> = vec![1, -2, -3, 4, 5];
let b = a
.into_iter() // into_iter() 会在 T 上遍历
.filter(|x: &i32| *x > 0); // x 的类型为 &i32,它源于 filter
let c: Vec<i32> = b.collect();
println!("{:?}", c) // [1, 4, 5]
}
为了让 closure 内部代码简单些,当使用 iter() 时,可以使用下面不同的形式:
let a: Vec<i32> = vec![1, -2, -3, 4, 5];
let mut iter = a.iter().filter(|x| **x > 1); // 形式一
let mut iter = a.iter().filter(|&x| *x > 1); // 同上,形式二
let mut iter = a.iter().filter(|&&x| x > 1); // 同上,形式三
当使用 into_iter() 时,可以使用下面不同的形式:
let a: Vec<i32> = vec![1, -2, -3, 4, 5];
let mut iter = a.into_iter().filter(|x| *x > 1); // 形式一
let mut iter = a.into_iter().filter(|&x| x > 1); // 同上,形式二
4.3. filter_map
filter_map 可以看作是 filter 和 map 的混合体,可以使代码更加简洁。
比如, 我们有一个字符串数组,现在要过滤出那些可以转换为数字的,并转换为数字。 这个任务可以通过先 map,再 filter,再 map 来实现:
fn main() {
let a = ["1", "two", "NaN", "four", "5"];
let mut iter = a.iter()
.map(|s| s.parse()) // 字符串转换为数字,提到 Result
.filter(|s| s.is_ok()) // 过滤可以 parse 成功的
.map(|s| s.unwrap()); // 上一步已经过滤了,直接 unwrap 可得到数字,不会失败
assert_eq!(iter.next(), Some(1));
assert_eq!(iter.next(), Some(5));
assert_eq!(iter.next(), None);
}
如果使用 filter_map ,则上面代码可以简化为:
fn main() {
let a = ["1", "two", "NaN", "four", "5"];
let mut iter = a.iter()
.filter_map(|s| s.parse().ok());
assert_eq!(iter.next(), Some(1));
assert_eq!(iter.next(), Some(5));
assert_eq!(iter.next(), None);
}
ok() 表示如果 Result 是成功的,返回 Some(X),否则返回 None;而 filter_map 会从 closure 返回结果中过滤出 Some(X),返回 X。
4.4. flatten
flatten 可以拍平嵌套的迭代器,下面是 flatten 的一个使用实例:
fn main() {
let data = vec![vec![1, 2, 3, 4], vec![5, 6]];
let flattened = data.into_iter().flatten().collect::<Vec<u8>>();
assert_eq!(flattened, &[1, 2, 3, 4, 5, 6]);
}
4.5. flat_map
flat_map 的参数是个 closure,这个 closure 返回一个迭代器,而 flat_map 会把 closure 返回的多个迭代器拍平 ,下面是个例子:
fn main() {
let lines_vec = vec!("hello:rust","how:are:you");
let words_vec = lines_vec.iter()
.flat_map(|x: &&str| (*x).split(":")) // split 返回的是迭代器
.collect::<Vec<&str>>();
println!("{:?}", words_vec); // ["hello", "rust", "how", "are", "you"]
}
下面是 flat_map 的另一个使用例子:
use std::collections::HashMap ;
fn main() {
let mut scores = HashMap::new() ;
scores.insert("alice", vec![70, 50, 90]);
scores.insert("bob", vec![70, 80, 90]);
scores.insert("carol", vec![40, 60, 70]);
let people = ["alice", "bob", "carol"];
let x: Vec<&i32> = people.iter()
.flat_map(|x| scores[x].iter()) // closure 返回迭代器,flat_map 把所有迭代器拍平
.collect();
println!("{:?}", x); // [70, 50, 90, 70, 80, 90, 40, 60, 70]
}
再看一个 flat_map 的使用例子:
fn main() {
let mut scores = HashMap::new() ;
scores.insert("alice", vec![70, 50, 90]);
scores.insert("bob", vec![70, 80, 90]);
scores.insert("carol", vec![40, 60, 70]);
let people = ["alice", "bob", "carol"];
let x: Vec<i32> = people.iter()
.flat_map(|x| scores[x].iter().map(|y| y + 1)) // closure 返回迭代器,flat_map 把所有迭代器拍平
.collect();
println!("{:?}", x); // [71, 51, 91, 71, 81, 91, 41, 61, 71]
}
4.6. 其它迭代器适配器
5. 迭代器消费者(触发遍历)
创建迭代器时不会遍历 Collection,只有遇到迭代器消费者时,才会触发遍历。常见的迭代器消费者有 collect, fold, count 等等。
5.1. collect
在 4 中已经介绍了 collect 的很多使用例子。使用 collect 可以把遍历迭代器时的数量收集到一个容器中。不过,我们需要指定你想收集到什么容器中,比如 Vec 或者 LinkedList 等,下面是使用例子:
use std::collections::LinkedList;
fn main() {
let a: [i32; 3] = [1, 2, 3];
let doubled: Vec<i32> = a.iter().map(|&x| x * 2).collect();
let doubled: LinkedList<i32> = a.iter().map(|&x| x * 2).collect();
let doubled = a.iter().map(|&x| x * 2).collect::<Vec<i32>>();
let doubled = a.iter().map(|&x| x * 2).collect::<LinkedList<i32>>();
// 省略元素类型
let doubled: Vec<_> = a.iter().map(|&x| x * 2).collect();
let doubled: LinkedList<_> = a.iter().map(|&x| x * 2).collect();
let doubled = a.iter().map(|&x| x * 2).collect::<Vec<_>>();
let doubled = a.iter().map(|&x| x * 2).collect::<LinkedList<_>>();
}
在指定类型时,编译器仅想知道哪种类型的容器(比如是 Vec 还是 LinkedList 或者其它),而容器元素类型(上面例子中的 i32)是可以推导出来的,所以不需要显示指定元素类型,如上面例子中的 Vec<i32> 可以简写为 Vec<_> 。
5.2. fold(Reduce 操作)
fold 用于从 Collection 数据得到单个数据,这也称为 Reduce 操作。 它接收 2 个参数,第 1 个参数是初始值,第 2 个参数是一个 closure。
下面是使用 fold 求和的例子(真实项目中直接使用 sum 即可,这里仅仅是演示 fold 用法):
fn main() {
let a = [1, 2, 3, 4];
// the sum of all of the elements of the array
let sum = a.iter().fold(0, |acc: i32, x: &i32| acc + x);
// ^ ^ ^
// | | |
// initial | element
// |
// accumulator
assert_eq!(sum, 10);
}
表 1 演示了上面代码执行时,closure 两个参数 acc 和 x 的变化情况。
| element | acc | x | result |
|---|---|---|---|
| 0 | |||
| 1 | 0 | 1 | 1 |
| 2 | 1 | 2 | 3 |
| 3 | 3 | 3 | 6 |
| 4 | 6 | 4 | 10 |
下面是使用 fold 分别实现 sum, count, product 的例子:
fn main() {
let a = [1, 2, 3, 4];
println!("sum {}", a.iter().fold(0, |acc, x| acc + x)); // sum 10
println!("count {}", a.iter().fold(0, |acc, _| acc + 1)); // count 4
println!("product {}", a.iter().fold(1, |acc, x| acc * x)); // product 24
}
5.3. 其它迭代器消费者
6. 第三方库
6.1. itertools
标准库已经提供了很多迭代器适配器和迭代器消费者,如果还不够用,可以看看 itertools。
6.2. rayon
如果你想要个并行库,可以使用 rayon 。把标准库中节 1.1 介绍的迭代器换为 rayon 中如表 2 所示的对应迭代器即可实现并行操作。
| 标准库 | rayon 并发库 |
|---|---|
| iter() | par_iter() |
| iter_mut() | par_iter_mut() |
| into_iter() | into_par_iter() |
下面是 rayon 的一个例子:
use rayon::prelude::*;
fn sum_of_squares(input: &[i32]) -> i32 {
input.par_iter() // <-- just change that!
.map(|&i| i * i)
.sum()
}