写点什么

5 分钟速读之 Rust 权威指南(二十二)迭代器

用户头像
码生笔谈
关注
发布于: 2021 年 06 月 15 日
5分钟速读之Rust权威指南(二十二)迭代器

迭代器

类似 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
复制代码


发布于: 2021 年 06 月 15 日阅读数: 8
用户头像

码生笔谈

关注

欢迎关注「码生笔谈」公众号 2018.09.09 加入

前端、Rust干货分享,一起成为更好的Javascripter & Rustacean

评论

发布
暂无评论
5分钟速读之Rust权威指南(二十二)迭代器