概念
- 闭包类似一种函数,可以赋值给变量或作为参数传递给其他函数,闭包可以捕获环境中的变量直接使用。
- 闭包不用注明参数类型,编译器会自动推断
- 如果闭包已经使用过,那么它的参数和返回值类型就是确定了的,如果再传递一个不同类型的变量,编译器会报错
使用结构体存储闭包
结构体的定义:
不同的闭包实际上是不同的匿名结构体,因此它们类型不同,所以这里要用泛型**T**
这里的是对捕获变量的不可变借用,可以使用Fn trait
,所以trait bound
为Fn(u32) -> u32
struct Cacher<T> where T: Fn(u32) -> u32{
calcu_closure: T,
value: Option<u32>
}
结构体实现:
impl<T> Cacher<T> where T: Fn(u32) -> u32{
fn new(calcu_closure: T) -> Cacher<T>{
// 根据closure创建一个cacher实例
Cacher {
calcu_closure,
value:None
}
}
// 拿到参数返回计算值
fn value(&mut self, intensity: u32) -> u32{
match self.value {
// 有缓存值
Some(v) => v,
None => {
// 没有缓存值,执行计算
let v = (self.calcu_closure)(intensity);
// 缓存起来
self.value = Some(v);
v
}
}
}
}
使用:
fn generate_workout(intensity:u32, num:u32) {
// 创建闭包实例
// 将闭包作为参数传递
let mut cacher = Cacher::new(|intensity|{
println!("calucate slowly");
thread::sleep(Duration::from_secs(4));
intensity
});
if intensity < 25 {
println!(
"Today, do {} pushups!",
cacher.value(intensity)
);
println!(
"Next, do {} situps!",
cacher.value(intensity)
);
} else {
if num == 3 {
println!("Take a break today! Remember to stay hydrated!");
} else {
println!(
"Today, run for {} minutes!",
cacher.value(intensity)
);
}
}
}
捕获环境变量
当闭包从环境(闭包周围的作用域被称为其 环境_environment_
)中捕获一个值,闭包会在闭包体中储存这个值以供使用。
闭包可以通过三种方式捕获其环境,这对应函数的三种获取参数的方式:获取所有权,可变借用和不可变借用。对应如下三个 Fn trait
:
FnOnce
消费从周围作用域捕获的变量,为了消费捕获到的变量,闭包必须获取其所有权并在定义闭包时将其移动进闭包。这代表它只能被调用一次, 所有闭包都实现了**FnOnce**
FnMut
获取可变的借用值所以可以改变其环境Fn
从其环境获取不可变的借用值
参数列表前使用move
关键字可以强制闭包获取其使用的环境值的所有权。
大部分需要指定一个Fn
系列trait bound
的时候,可以从Fn
开始,而编译器会根据闭包体中的情况告诉你是否需要FnMut
、FnOnce
。