闭包与迭代器-Rust
闭包与迭代器-Rust
xiu闭包
定义格式
|参数|{语句}
closure,一种匿名函数,可以储存在变量里的类似函数的结构
1 | fn main() { |
闭包类型推导
Rust是静态语言,所有的变量都有类型
可以显示的为闭包定义类型
1 | fn main() { |
省略的写法
1 | fn add_one_v1 (x: u32) -> u32 { x + 1 } |
闭包不会作为接口提供给外部使用,所以在定义的时候可以不确认参数类型,让编译器帮我们推导参数类型,需要注意的是,根据闭包调用时参数的类型进行判断,类型确认后就不可以修改了
闭包中最后一行表达式返回的值,就是闭包执行后的返回值
结构体当中的闭包
实现一个简易缓存,获取一个值,将其缓存
1、一个闭包用于获取值
2、一个变量,用于存储数值
1 | // 定义一个泛型结构体 Cacher,用于缓存闭包的运算结果 |
方法调用:self.method(arg)
(调用结构体的方法)
闭包调用:(self.closure)(arg)
(调用结构体的闭包字段)
捕获作用域的值
闭包可以捕获调用者作用域的局部变量,而函数不可以捕捉上层函数的局部变量
三种Fn特性,函数参数的三种传入方式:转移所有权、可变借用、不可变借用
FnOnce
,该类型的闭包会拿走被捕获变量的所有权。
FnMut
,它以可变借用的方式捕获了环境中的值
Fn
特征,它以不可变借用的方式捕获环境中的值
三种特征的关系
- 所有的闭包都自动实现了
FnOnce
特征,因此任何一个闭包都至少可以被调用一次 - 没有移出所捕获变量的所有权的闭包自动实现了
FnMut
特征 - 不需要对捕获变量进行改变的闭包自动实现了
Fn
特征
一个闭包实现了哪种 Fn 特征取决于该闭包如何使用被捕获的变量,而不是取决于闭包如何捕获它们
闭包作为函数返回值
1 | fn factory() -> Fn(i32) -> i32 {//报错 |
Rust 要求函数的参数和返回类型,必须有固定的内存大小,根据编译器提示,可以修改为
1 | fn factory(x:i32) -> impl Fn(i32) -> i32 {//if 和 else 分支中返回了不同的闭包类型 |
迭代器
一种处理元素序列的方式,迭代器就是用来按顺序访问集合中的元素(比如数组、列表、文件内容等)的工具
for循环与迭代器
迭代器跟 for
循环颇为相似,都是去遍历一个集合,但是实际上它们存在不小的差别,其中最主要的差别就是:是否通过索引来访问集合
如下是js的循环
rust循环
1 | fn main() { |
区别在于Rust没有使用索引,他把arr当成了迭代器,直接遍历其中的元素
惰性初始化
1 | fn main() { |
惰性初始化的方式确保了创建迭代器不会有任何额外的性能损耗
next方法
迭代器之所以成为迭代器,就是因为实现了 Iterator
特征(trait),要实现该特征,最主要的就是实现其中的 next
方法,该方法控制如何从集合中取值,最终返回值的类型是关联类型 Item
。
for
循环通过不停调用迭代器上的 next
方法,来获取迭代器中的元素。
1 | fn main() { |
next 方法对迭代器的遍历是消耗性的,每次消耗它一个元素,最终迭代器中将没有任何元素,只能返回 None
next
方法返回的是Option
类型,当有值时返回Some(i32)
,无值时返回None
遍历是按照迭代器中元素的排列顺序依次进行的,因此我们严格按照数组中元素的顺序取出了
Some(1)
,Some(2)
,Some(3)
手动迭代必须将迭代器声明为
mut
可变,因为调用next
会改变迭代器其中的状态数据(当前遍历的位置等),而for
循环去迭代则无需标注mut
,因为它会帮我们自动完成IntoIterator 特征
迭代器自身也实现了
IntoIterator
,标准库
1 | fn main() { |
into_iter, iter, iter_mut
在之前的代码中,我们统一使用了 into_iter
的方式将数组转化为迭代器,除此之外,还有 iter
和 iter_mut
,聪明的读者应该大概能猜到这三者的区别:
into_iter
会夺走所有权,返回具有所有权的值iter
是借用iter_mut
是可变借用
into_
之类的,都是拿走所有权,_mut
之类的都是可变借用,剩下的就是不可变借用
Iterator 和 IntoIterator 的区别
Iterator
就是迭代器特征,只有实现了它才能称为迭代器,才能调用 next
IntoIterator
强调的是某一个类型如果实现了该特征,它可以通过 into_iter
,iter
等方法变成一个迭代器。
消费者与适配器
调用next方法的方法被称为消耗型适配器,消耗这些方法会消耗item(迭代器成员)
sum方法,消耗型适配器
map方法,迭代器适配器
不会消耗原始迭代器,通过修改原始迭代器生成新的迭代器
1 | fn main() { |
1 | fn main() { |
很多迭代器适配器都将闭包作为自己的参数,filter方法