闭包

闭包是一种匿名函数,它可以赋值给变量也可以作为参数传递给其它函数,不同于函数的是,它允许捕获调用者作用域中的值.

当闭包从环境中捕获一个值时,会分配内存去存储这些值。对于有些场景来说,这种额外的内存分配会成为一种负担。与之相比,函数就不会去捕获这些环境值,因此定义和使用函数不会拥有这种内存负担。

闭包的类型推导

闭包享受编译器的类型推导能力,无需标注参数和返回值的类型。但是它不是泛型,当编译器推导出一种类型后,它就会一直使用该类型

fn main() {
let example_closure = |x| x;
let s = example_closure(String::from("hello"));
let n = example_closure(5);

在 s 中,编译器为 x 推导出类型 String,但是紧接着 n 试图用 5 这个整型去调用闭包,跟编译器之前推导的 String 类型不符

三种 Fn 特征

  • FnOnce,该类型的闭包会拿走被捕获变量的所有权。Once 顾名思义,说明该闭包只能运行一次 强制闭包取得捕获变量的所有权,可以在参数列表前添加 move 关键字,这种用法通常用于闭包的生命周期大于捕获变量的生命周期时,例如将闭包返回或移入其他线程。
    #![allow(unused)]
    fn main() {
      use std::thread;
      let v = vec![1, 2, 3];
      let handle = thread::spawn(move || {
          println!("Here's a vector: {:?}", v);
      });
      handle.join().unwrap();
    }
  • FnMut,它以可变借用的方式捕获了环境中的值,因此可以修改该值
  • Fn 特征,它以不可变借用的方式捕获环境中的值

一个闭包实现了哪种 Fn 特征取决于该闭包如何使用被捕获的变量,而不是取决于闭包如何捕获它们。move 本身强调的就是后者,闭包如何捕获变量

三种 Fn 的关系

  • 所有的闭包都自动实现了 FnOnce 特征,因此任何一个闭包都至少可以被调用一次
  • 没有移出所捕获变量的所有权的闭包自动实现了 FnMut 特征
  • 不需要对捕获变量进行改变的闭包自动实现了 Fn 特征