Rust 的闭包可以近似看成一个包含捕获到的变量的结构体. 官方例子类似这样:

fn f<F : FnOnce() -> String> (g: F) {
    println!("{}", g());
}

let mut s = String::from("foo");
let t = String::from("bar");

f(|| {
    s += &t;
    s
});
// Prints "foobar".

就会转成类似这样的结构体:

struct Closure<'a> {
    s : String,
    t : &'a String,
}

impl<'a> FnOnce<()> for Closure<'a> {
    type Output = String;
    fn call_once(self) -> String {
        self.s += &*self.t;
        self.s
    }
}

闭包相关的 trait

在闭包里面有三个相关的 trait:FnOnce, FnMut, Fn. 他们分别对应闭包的三种调用方式:占用所有权,可变引用,不可变引用。他们的定义分别这样:

pub trait FnOnce<Args>
where
    Args: Tuple,
{
    type Output;

    // Required method
    extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
}

pub trait FnMut<Args>: FnOnce<Args>
where
    Args: Tuple,
{
    // Required method
    extern "rust-call" fn call_mut(
        &mut self,
        args: Args
    ) -> Self::Output;
}

pub trait Fn<Args>: FnMut<Args>
where
    Args: Tuple,
{
    // Required method
    extern "rust-call" fn call(&self, args: Args) -> Self::Output;
}

总结一下就是:

  1. 如果没有任何捕获变量,则实现 FnOnce
  2. 如果有捕获变量,并且会对捕获变量进行修改,则实现 FnMut
  3. 如果有捕获变量,但不会对捕获变量进行修改,则实现 Fn
  4. 编译器会把 FnOnce 当成 fn(T) 函数指针去看待

闭包实现的 Trait

闭包自身实现的 Trait:

  1. Sized
  2. Copy/Clone
  3. Sync/Send