迭代器
类似 JS 中数组的 forEach,map 等迭代方法,在 rust 中,也有迭代器的概念,区别是 rust 的 for_each,map 方法被称之为迭代适配器,迭代适配器是惰性的(lazy)。除非你主动调用,否则它们不会产生任何的实际效果。
创建迭代器
使用 iter 方法创建一个迭代器,并调用 next 进行消费:
let arr = vec![1, 2, 3];
// 创建迭代器// 由于迭代器内部有维护指针位置,每次迭代会更新指针,所以需要标记mutlet mut iter = v1.iter();
// 消费迭代器println!("{:?}", iter.next());println!("{:?}", iter.next());println!("{:?}", iter.next());println!("{:?}", iter.next());
// Some(1)// Some(2)// Some(3)// None
复制代码
其实和 JS 中的很像,可以回忆一下:
var arr= [1,2,3];
// 创建迭代器var iter = arr[Symbol.iterator]();
// 消费迭代器iter.next();iter.next();iter.next();iter.next();
// {value: 1, done: false}// {value: 2, done: false}// {value: 3, done: false}// {value: undefined, done: true}
复制代码
消费迭代器
除了使用 next 方法手动调用迭代器以外,还可以使用 for 循环来消费,过程中迭代的结果如果是 Some 类型,for 循环会继续迭代,直到遇到 None 才会停止:
let arr = vec![1, 2, 3];let iter = arr.iter();
// 因为for循环会消费迭代器,所以会取得iter的所有权for item in iter { println!("{}", item) // 1 // 2 // 3}
println!("{:?}", iter) // 报错,所有权已经被移动
复制代码
迭代器 trait
并不是所有类型都可以进行迭代,能够迭代的类型都需要实现 Iterator trait:
pub trait Iterator { // 会在后边介绍:关联类型 type Item; // 每一个迭代器都拥有next方法, // 它会在每次被调用时返回一个包裹在Some中的迭代器元素, // 并在迭代结束时返回None。 fn next(&mut self) -> Option<Self::Item>;}
复制代码
为自定义结构体实现迭代器 trait:
// 定义一个计数器结构体struct Counter { // 迭代最大值 max: u32, // 当前值 current: u32}
// 实现迭代器traitimpl Iterator for Counter { // 指定每次迭代的类型 type Item = u32;
// 实现next方法 fn next(&mut self) -> Option<Self::Item>{ // 当前值大于等于最大值时,也就是迭代结束的时候,返回None if self.current >= self.max { None } else { // 获取当前值 let val = self.current; // 为下次迭代累加1 self.current += 1; // 返回当前值 Some(val) } }}
let counter = Counter { current: 0, max: 3};
// 可以直接使用for循环for item in counter { println!("{}", item); // 1 // 2 // 3}
复制代码
迭代器类型
在消费 iter 迭代器时,我们取到的是每一个成员的不可变引用,事实上一共有三种迭代器可用:
iter:获得成员的不可变引用
iter_mut:获得成员的可变引用
into_iter:获得成员的所有权
消费适配器
rust 内置了许多消费迭代器的方法,这些方法会获取迭代器的所有权并反复调用 next 来遍历元素,例如 sum 方法对数组中的成员求和:
let arr = [1, 2, 3];let iter = v3.iter();
// sum方法会取得迭代器所有权(可以看一下sum的签名),// 所以迭代器无法再继续使用let total: i32 = iter.sum();println!("{}", total); // 6
复制代码
迭代器适配器
rust 还内置了许多用于通过迭代器链式操作成员的方法,这些方法都是惰性的,只有在最后调用一个消耗适配器的方法后,迭代适配器方法才会执行,例如 map 迭代器适配器:
let arr = [1, 2, 3];// map好创建一个新的迭代器并返回,// 由于迭代器适配器是惰性的,除非消费迭代器,否则什么事情都不会发生,// 因为没有进行消费,此时编译会报提醒arr.iter().map(|x| x + 1);
复制代码
使用 collect 消费适配器:
// collect会消耗迭代器并将结果值收集到某种集合数据类型中let arr = [1, 2, 3];
// 注意使用collect需要指定最终类型let arr2: Vec<i32> = arr.iter().map(|x| x + 1).collect();// 等价于let arr2 = arr.iter().map(|x| x + 1).collect::<Vec<i32>>();
println!("{:?}", arr2); // [2, 3, 4]
复制代码
filter 迭代适配器用于过滤成员:
#[derive(Debug)]struct Shoe { size: u32}let arr = vec![ Shoe { size: 40 }, Shoe { size: 41 }, Shoe { size: 42 },];
// filter将过滤size大于40的Shoelet arr2: Vec<_> = arr.iter().filter(|x| x.size > 40).collect();
println!("{:?}", arr2);// [Shoe { size: 41 }, Shoe { size: 42 }]
复制代码
一个更复杂的迭代适配器例子:
// 使用上面自定义结构体Counterlet sum: u32 = Counter { current: 0, max: 5}// zip:组合成: (1,2) (2,3) (3,4) (4,5),由于最后一个(5, None)由于其中一个是None,将不会被生成.zip(Counter { current: 0, max: 5 // skip:跳过第一个值,也就是与 1,2,3,4进行zip操作}.skip(1))// map映射后:2 6 12 20.map(|(a, b)| a * b)// filter过滤后:6 12.filter(|x| x % 3 == 0)// sum求和:6 + 12.sum();
println!("{}", sum); // 18
复制代码
评论