💻博主现有专栏:
C51单片机(STC89C516),c语言,c++,离散数学,算法设计与分析,数据结构,Python,Java基础,MySQL,linux,基于HTML5的网页设计及应用,Rust(官方文档重点总结),jQuery,前端vue.js,Javaweb开发,Python机器学习等
🥏主页链接:Y小夜-CSDN博客
目录
🎯Iterator trait和next方法
🎯消费迭代器的方法
🎯产生其他迭代器的方法
🎯使用捕获其环境的闭包
迭代器模式允许你对一个序列的项进行某些处理。迭代器(iterator)负责遍历序列中的每一项和决定序列何时结束的逻辑。当使用迭代器时,我们无需重新实现这些逻辑。
在 Rust 中,迭代器是 惰性的(lazy),这意味着在调用方法使用迭代器之前它都不会有效果。
let v1 = vec![1, 2, 3];let v1_iter = v1.iter();
迭代器被储存在
v1_iter
变量中。一旦创建迭代器之后,可以选择用多种方式利用它。在第三章的示例 3-5 中,我们使用for
循环来遍历一个数组并在每一个项上执行了一些代码。在底层它隐式地创建并接着消费了一个迭代器,不过直到现在我们都一笔带过了它具体是如何工作的。例子将迭代器的创建和
for
循环中的使用分开。迭代器被储存在v1_iter
变量中,而这时没有进行迭代。一旦for
循环开始使用v1_iter
,接着迭代器中的每一个元素被用于循环的一次迭代,这会打印出其每一个值:let v1 = vec![1, 2, 3];let v1_iter = v1.iter();for val in v1_iter {println!("Got: {}", val);}
在标准库中没有提供迭代器的语言中,我们可能会使用一个从 0 开始的索引变量,使用这个变量索引 vector 中的值,并循环增加其值直到达到 vector 的元素数量。
🎯Iterator trait和next方法
迭代器都实现了一个叫做
Iterator
的定义于标准库的 trait。这个 trait 的定义看起来像这样:pub trait Iterator {type Item;fn next(&mut self) -> Option<Self::Item>;// 此处省略了方法的默认实现 }
注意这里有一个我们还未讲到的新语法:
type Item
和Self::Item
,它们定义了 trait 的 关联类型(associated type)。
next
是Iterator
实现者被要求定义的唯一方法。next
一次返回迭代器中的一个项,封装在Some
中,当迭代器结束时,它返回None
。#[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);}
注意
v1_iter
需要是可变的:在迭代器上调用next
方法改变了迭代器中用来记录序列位置的状态。换句话说,代码 消费(consume)了,或使用了迭代器。每一个next
调用都会从迭代器中消费一个项。使用for
循环时无需使v1_iter
可变因为for
循环会获取v1_iter
的所有权并在后台使v1_iter
可变。
🎯消费迭代器的方法
Iterator
trait 有一系列不同的由标准库提供默认实现的方法;你可以在Iterator
trait 的标准库 API 文档中找到所有这些方法。一些方法在其定义中调用了next
方法,这也就是为什么在实现Iterator
trait 时要求实现next
方法的原因。这些调用
next
方法的方法被称为 消费适配器(consuming adaptors),因为调用它们会消耗迭代器。一个消费适配器的例子是sum
方法。#[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);}
调用
sum
之后不再允许使用v1_iter
因为调用sum
时它会获取迭代器的所有权。
🎯产生其他迭代器的方法
Iterator
trait 中定义了另一类方法,被称为 迭代器适配器(iterator adaptors),它们允许我们将当前迭代器变为不同类型的迭代器。可以链式调用多个迭代器适配器。不过因为所有的迭代器都是惰性的,必须调用一个消费适配器方法以便获取迭代器适配器调用的结果。let v1: Vec<i32> = vec![1, 2, 3];v1.iter().map(|x| x + 1);
不过这些代码会产生一个警告
$ cargo runCompiling iterators v0.1.0 (file:///projects/iterators) warning: unused `Map` that must be used--> src/main.rs:4:5| 4 | v1.iter().map(|x| x + 1);| ^^^^^^^^^^^^^^^^^^^^^^^^^|= note: `#[warn(unused_must_use)]` on by default= note: iterators are lazy and do nothing unless consumedwarning: `iterators` (bin "iterators") generated 1 warningFinished dev [unoptimized + debuginfo] target(s) in 0.47sRunning `target/debug/iterators`
为了修复这个警告并消费迭代器获取有用的结果,我们将使用第十二章示结合
env::args
使用的collect
方法。这个方法消费迭代器并将结果收集到一个数据结构中。let v1: Vec<i32> = vec![1, 2, 3];let v2: Vec<_> = v1.iter().map(|x| x + 1).collect();assert_eq!(v2, vec![2, 3, 4]);
因为
map
获取一个闭包,可以指定任何希望在遍历的每个元素上执行的操作。这是一个展示如何使用闭包来自定义行为同时又复用Iterator
trait 提供的迭代行为的绝佳例子。可以链式调用多个迭代器适配器来以一种可读的方式进行复杂的操作。不过因为所有的迭代器都是惰性的,你需要调用一个消费适配器方法从迭代器适配器调用中获取结果。
🎯使用捕获其环境的闭包
很多迭代器适配器接受闭包作为参数,而通常指定为迭代器适配器参数的闭包会是捕获其环境的闭包。
#[derive(PartialEq, Debug)] struct Shoe {size: u32,style: String, }fn shoes_in_size(shoes: Vec<Shoe>, shoe_size: u32) -> Vec<Shoe> {shoes.into_iter().filter(|s| s.size == shoe_size).collect() }#[cfg(test)] mod tests {use super::*;#[test]fn filters_by_size() {let shoes = vec![Shoe {size: 10,style: String::from("sneaker"),},Shoe {size: 13,style: String::from("sandal"),},Shoe {size: 10,style: String::from("boot"),},];let in_my_size = shoes_in_size(shoes, 10);assert_eq!(in_my_size,vec![Shoe {size: 10,style: String::from("sneaker")},Shoe {size: 10,style: String::from("boot")},]);} }
shoes_in_my_size
函数获取一个鞋子 vector 的所有权和一个鞋子大小作为参数。它返回一个只包含指定大小鞋子的 vector。
shoes_in_my_size
函数体中调用了into_iter
来创建一个获取 vector 所有权的迭代器。接着调用filter
将这个迭代器适配成一个只含有那些闭包返回true
的元素的新迭代器。