闭包能够从定义它的上下文环境中捕获值。
闭包的语法:
|参数列表| {
// 略
}闭包是不强制要求标注参数和返回值的类型的,这里有这样几个原因:
- 闭包不需要对外暴露
- 闭包只在狭小的上下文中使用,编译器能够可靠推出类型。
- 闭包是很简短的,如果要求标注类型非常影响编码体验。 不过如果要显式标注也是可以的。
闭包内部体只有一条表达式的话,可以省略花括号。
使用泛型参数和Fn trait来存储闭包
用一个结构体保存结果和闭包,当闭包没有运行结果的时候才运行闭包,有结果就直接复用。这种模式被称为记忆化或惰性求值。
为了将闭包存储在结构体中,需要指明闭包的类型。而每一个闭包都具有自己的匿名类型(即使两个闭包的签名一致,类型也是不同的)。因此要表达一个闭包的类型就需要使用 trait约束 了。
所有的闭包都实现了Fn、FnMut、FnOnce trait中的一个。
定义一个存储闭包和其结果的结构体:
struct Cacher<T>
where T: Fn(u32) -> u32
{
calculation: T,
value: Option<u32>,
}为其实现new函数以及惰性求值方法
impl <T> Cacher<T>
where T:Fn(u32)->u32
{
fn new(calculation: T) -> Cacher<T> {
Cacher {
calculation,
value: None,
}
}
fn value(&mut self, arg) -> u32 {
match self.value {
Some(v) => v,
None => {
let v = (self.calculation)(arg);
self.value = Some(v);
v
},
}
}
}捕获上下文环境
闭包可以捕获其上下文环境中的变量,例如
fn main {
let x = 4;
let equal_to_x = |a| {
a == x
}
}闭包捕获值,会使用额外的空间来存储这些值以便在闭包体内使用。相当于隐式定义了一个结构体用来存储捕获的值,然后为这个结构体实现了一个方法,就是闭包函数体。
闭包捕获值与函数接收参数的3中方式是一致的:
- 获得所有权
- 可变借用
- 不可变借用
这也与三种trait对应:
- FnOnce:消耗捕获的变量,需要获取捕获值的所有权。同一变量的所有权不能被多次获取并消耗,因此只能调用一次。
- FnMut:从环境中可变借用值。
- Fn:从环境中不可变借用值。
Rust会根据闭包中使用值的方式来自动推导出它需要使用的trait。 所有的闭包都实现了FnOnce,至少可以调用一次。不需要移动捕获变量的实现了FnMut,不需要对捕获变量进行可变访问的闭包同时实现了Fn。
假如你希望强制闭包获取环境中值的所有权,那么你可以在参数列表前添加move关键字。
fn main() {
let x = vec![1, 2, 3];
let equal_to_x = move |z| z == x;
}tags: 函数式编程