Iterator
迭代器指的是任何实现 std::iter::Iterator trait的值。这个Iterator trait的定义如下:
trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
... // 其他默认方法
}
Item 是迭代器产生值的类型。我们只需要实现一个方法——next() , 它返回一个Option<Item>。 一般情况返回Some(Item) ; 如果迭代完成,就返回None。Iterate trait中包含了很多默认的方法,不需要我们自己去实现。
IntoIterator
如果有其他类型,也想迭代怎么办,只需要实现std::iter::IntoIterator就可以了。它的 into_iter 方法接收一个值,然后基于这个值返回一个迭代器:
trait IntoIterator where Self::IntoIter::Item == Self::Item {
type Item;
type IntoIter: Iterator;
fn into_iter(self) -> Self::IntoIter;
}
IntoIter 是迭代器值本身的类型,而 Item 是它产生值的类型。我们称任何实现 IntoIterator 的类型为可迭代类型(iterable),因为可以在需要的时候通过循环来访问它。
迭代器的使用
我们可以直接在迭代器上调用next方法。
fn iterator_demonstration() {
let v1 = vec![1, 2, 3];
let mut iter = v1.iter();
assert_eq!(iter.next(), Some(&1));
assert_eq!(iter.next(), Some(&2));
assert_eq!(iter.next(), Some(&3));
assert_eq!(iter.next(), None);
}
我们可以手工直接调用迭代器的next() 方法,然后使用while let语法来做循环。
fn main() {
let v = vec![1,2,3,4,5];
let mut iter = v.iter();
while let Some(i) = iter.next() {
println!("{}", i);
}
}
这两个例子中的iter必须是可变的,因为调用next方法改变了迭代器内部用来记录序列位置的状态。换句话说,这段代码消耗或使用了迭代器,每次调用next都吃掉了迭代器中的一个元素。
实际上, Rust里面更简洁、 更自然地使用迭代器的方式是使用for循环。 本质上来说, for循环就是专门为迭代器设计的一个语法糖。 for循环可以对针对数组切片、 字符串、Range、 Vec、 LinkedList、 HashMap、 BTreeMap等所有具有迭代器的类型执行循环, 而且还允许我们针对自定义类型实现循环.
use std::collections::HashMap; fn main() {
let v = vec![1,2,3,4,5,6,7,8,9];
for i in v {
println!("{}", i);
}
let map : HashMap<i32, char> =
[(1, 'a'), (2, 'b'), (3, 'c')].iter().cloned().collect();
for (k, v) in &map {
println!("{} : {}", k, v);
}
}
在for循环中我们之所以不要求变量v可变,是因为循环取得了v的所有权并在内部使得它可变了;map又可以使用&。那么for循环是怎么做到这一点的呢? 原因就是 IntoIterator 这个trait
只要某个类型实现了IntoIterator, 那么调用into_iter() 方法就可以得到对应的迭代器。 这个into_iter() 方法的receiver是self, 而不是&self, 执行的是move语义。 这么做,可以同时支持Item类型为T、 &T或者&mut T, 用户有选择的权力。
// container在循环之后生命周期就结束了,循环过程中的每个item是从container中move出来的
for item in container {}
// 迭代器中只包含container的&型引用,循环过程中的每个item都是container中元素的借用
for item in &container {}
// 迭代器中包含container的&mut型引用,循环过程中的每个item都是指向container中元素的可变借用
for item in &mut container {}
Rust的for<item>in<container>{<body>}语法结构就是一个语法糖。 这个语法的原理其实就是调用<container>.into_iter() 方法来获得迭代器, 然后不断循环调用迭代器的next() 方法, 将返回值解包,赋值给<item>, 然后调用<body>语句块。
Rust的IntoIterator trait实际上就是for语法的扩展接口。 如果我们需要让各种自定义容器也能在for循环中使用, 那就可以借鉴标准库中的写法, 自行实现这个trait即可。