概念

  • 闭包类似一种函数,可以赋值给变量或作为参数传递给其他函数,闭包可以捕获环境中的变量直接使用。
  • 闭包不用注明参数类型,编译器会自动推断
  • 如果闭包已经使用过,那么它的参数和返回值类型就是确定了的,如果再传递一个不同类型的变量,编译器会报错

使用结构体存储闭包

结构体的定义
不同的闭包实际上是不同的匿名结构体,因此它们类型不同,所以这里要用泛型**T**
这里的是对捕获变量的不可变借用,可以使用Fn trait,所以trait boundFn(u32) -> u32

  1. struct Cacher<T> where T: Fn(u32) -> u32{
  2. calcu_closure: T,
  3. value: Option<u32>
  4. }

结构体实现:

  1. impl<T> Cacher<T> where T: Fn(u32) -> u32{
  2. fn new(calcu_closure: T) -> Cacher<T>{
  3. // 根据closure创建一个cacher实例
  4. Cacher {
  5. calcu_closure,
  6. value:None
  7. }
  8. }
  9. // 拿到参数返回计算值
  10. fn value(&mut self, intensity: u32) -> u32{
  11. match self.value {
  12. // 有缓存值
  13. Some(v) => v,
  14. None => {
  15. // 没有缓存值,执行计算
  16. let v = (self.calcu_closure)(intensity);
  17. // 缓存起来
  18. self.value = Some(v);
  19. v
  20. }
  21. }
  22. }
  23. }

使用:

  1. fn generate_workout(intensity:u32, num:u32) {
  2. // 创建闭包实例
  3. // 将闭包作为参数传递
  4. let mut cacher = Cacher::new(|intensity|{
  5. println!("calucate slowly");
  6. thread::sleep(Duration::from_secs(4));
  7. intensity
  8. });
  9. if intensity < 25 {
  10. println!(
  11. "Today, do {} pushups!",
  12. cacher.value(intensity)
  13. );
  14. println!(
  15. "Next, do {} situps!",
  16. cacher.value(intensity)
  17. );
  18. } else {
  19. if num == 3 {
  20. println!("Take a break today! Remember to stay hydrated!");
  21. } else {
  22. println!(
  23. "Today, run for {} minutes!",
  24. cacher.value(intensity)
  25. );
  26. }
  27. }
  28. }

捕获环境变量

当闭包从环境(闭包周围的作用域被称为其 环境_environment_)中捕获一个值,闭包会在闭包体中储存这个值以供使用。

闭包可以通过三种方式捕获其环境,这对应函数的三种获取参数的方式:获取所有权,可变借用和不可变借用。对应如下三个 Fn trait

  • FnOnce消费从周围作用域捕获的变量,为了消费捕获到的变量,闭包必须获取其所有权并在定义闭包时将其移动进闭包。这代表它只能被调用一次, 所有闭包都实现了**FnOnce**
  • FnMut获取可变的借用值所以可以改变其环境
  • Fn从其环境获取不可变的借用值

参数列表前使用move关键字可以强制闭包获取其使用的环境值的所有权

大部分需要指定一个Fn系列trait bound的时候,可以从Fn开始,而编译器会根据闭包体中的情况告诉你是否需要FnMutFnOnce