前言#
昨天我们学了闭包
今天我们往下学Iterator
Iterator#
迭代器相信大家之前有接触过。
在rust中,迭代器是lazy的,也就是惰性的。在你把这个迭代器消耗(consume)完之前,它是不会做任何影响的。
比如:
fn main() {
let v1 = vec![1, 2, 3];
let v1_iter = v1.iter();
}调用集合类型都有的iter方法创建一个迭代器。这个是完全没有影响的。
然后我们来用个for遍历他
fn main() {
let v1 = vec![1, 2, 3];
let v1_iter = v1.iter();
for val in v1_iter {
println!("Got: {}", val);
}
}
这个时候,每当超过当前item作用域之后这个iterator就会被消耗一层,然后再进入下一层item。
iterator trait和next方法#
所有的迭代器都实现了标准库里一个叫Iterator的trait。 大概张这个样子,由于截图太长了,没法放进来
#![allow(unused)]
fn main() {
pub trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
// methods with default implementations elided
}
}这里有一个我们并不认识的语法type Item,实际上是后面会学到的associate type也就是关联类型。
它要求我们在impl这个Iterator的时候也需要定义一个自己的Item类型。它会被用在next这个方法中,以Some(Item)的方式返回。
另外Iterator 这个trait只需要我们在实现的时候把next这个方法实现即可。 这个next的方法会在每一次迭代的时候返回一个Some(Item),然后在迭代结束的时候返回None。
我们可以直接对迭代器的实例使用next这个方法,比如
#[cfg(test)]
mod tests {
#[test]
fn iterator_demonstration() {
let v1 = vec![1, 2, 3];
let mut v1_iter = v1.iter();
assert_eq!(v1_iter.next(), Some(&1));
assert_eq!(v1_iter.next(), Some(&2));
assert_eq!(v1_iter.next(), Some(&3));
assert_eq!(v1_iter.next(), None);
}
}需要注意的一点是迭代器得是mut的。
iter这个method返回的是不可变的借用,如果想要获得所有权,可以调用into_iter,会返回不可变的所有权,如果想要可变的可以调用into_mut。
会消耗(consume)迭代器的method#
Iterator有一堆api是用来消耗迭代器的,其中最经典的自然就是next这个method。
rust团队管这种method叫做*consuming adaptors,*也就是消耗适配器。
每次调用都会消耗一个迭代器。
另外还有一个是sum,它会获得迭代器的所有权(记住这一点),然后迭代这个迭代器里所有的项通过重复的调用next方法,把每个项加起来最后返回。
我们来看个例子
#[cfg(test)]
mod tests {
#[test]
fn iterator_sum() {
let v1 = vec![1, 2, 3];
let v1_iter = v1.iter();
let total: i32 = v1_iter.sum();
assert_eq!(total, 6);
}
}

会产生(produce)其它迭代器的method#
Iterator adaptors, 也就是迭代器适配器,它们不会消耗迭代器。
相反,它们会返回一个其它的迭代器通过某些方面改动原来的迭代器。
比如map方法,它相信大家应该不陌生,ES6+中提供的高级函数(指接收一个function作为参数)。
rust中的map是迭代器的method,它是一个迭代器适配器,也就是说不会消耗迭代器并且最终会返回一个新的迭代器。
来看下例子
fn main() {
let v1: Vec<i32> = vec![1, 2, 3];
v1.iter().map(|x| x + 1);
}它接受一个闭包作为参数,并且迭代器的项会作为参数传入闭包中。

看了源码应该可以知道并不会立即执行闭包,而是得等新的迭代器被使用的时候才会执行。
也就是前面说的:lazy,只有在调用消耗适配器的时候才会执行。
我们可以直接调用collect方法把这个迭代器转换为一个集合,这也是一种consume。
fn main() {
let v1: Vec<i32> = vec![1, 2, 3];
let collect: Vec<_> = v1.iter().map(|x| x + 1).collect();
dbg!(collect);
}

然后我们还可以链式调用,比如map(xxx).filter(....).collect,因为迭代器适配器最终都是返回一个迭代器,所以不用担心链会断掉。 最后再用消耗适配器来消耗掉迭代器即可。
fn main() {
let v1: Vec<i32> = vec![1, 2, 3];
let collect: Vec<_> = v1.iter().map(|x| x + 1).filter(|x| x != &2).collect();
dbg!(collect);
}

总结#
迭代器还是比较简单的,不过大多数时候迭代器是和闭包一起使用的,我们这里涉及不多. 不过前路漫漫,早晚遇到.
参考#
- ^rust-iterator https://doc.rust-lang.org/book/ch13-02-iterators.html#processing-a-series-of-items-with-iterators
- ^rust-iterator-trait-and-next-method https://doc.rust-lang.org/book/ch13-02-iterators.html#the-iterator-trait-and-the-next-method
- ^rust-13.2-method-that-consume-iterator https://doc.rust-lang.org/book/ch13-02-iterators.html#methods-that-consume-the-iterator
发布于 2022-12-28 11:58・IP 属地广东
