概念
- 闭包类似一种函数,可以赋值给变量或作为参数传递给其他函数,闭包可以捕获环境中的变量直接使用。
- 闭包不用注明参数类型,编译器会自动推断
- 如果闭包已经使用过,那么它的参数和返回值类型就是确定了的,如果再传递一个不同类型的变量,编译器会报错
使用结构体存储闭包
结构体的定义:
不同的闭包实际上是不同的匿名结构体,因此它们类型不同,所以这里要用泛型**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。
