符合 Fn, FnMut and FnOnce 的闭包实现

不同类型的闭包可以被指定为不同类型的 trait. 可参考rust by example 中的相关段落.

StackOverflow 中的回答更明白的解释了 Fn, FnMut, FnOnce 的区别:

  • Fn: 是闭包的基本 Trait, 即闭包中仅有 reference ( &self)
  • FnMut: 旨在强调传入的闭包中含有可 reference ( &mut self)
  • FnOnce: 旨在强调传入的闭包中含有 moved 进来的变量 ( self), 即意味着这个闭包只能被调用一次.

从 Rust By Example 中的例子可以看到一个符合 FnOnce 的闭包的构建方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let mut farewell = "goodbye".to_owned();

// Capture 2 variables: `greeting` by reference and
// `farewell` by value.
let diary = || {
// `greeting` is by reference: requires `Fn`.
println!("I said {}.", greeting);

// Mutation forces `farewell` to be captured by
// mutable reference. Now requires `FnMut`.
farewell.push_str("!!!");
println!("Then I screamed {}.", farewell);
println!("Now I can sleep. zzzzz");

// Manually calling drop forces `farewell` to
// be captured by value. Now requires `FnOnce`.
mem::drop(farewell);
};

但是这个例子过于复杂, 一个最大的疑惑就是 FnOnce 的实现是否真的那么复杂? 我该如何自己定义一个 FnOnce闭包? 后来我发现其实非常简单, 只需要一个 move. 下面是我对三种 trait 的简单定义已经实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
fn apply_fn<F>(f: F)
where
F: Fn(),
{
f();
}

fn apply_fn_mut<F>(mut f: F)
where
F: FnMut(),
{
f();
}

fn apply_fn_once<F>(f: F)
where
F: FnOnce(),
{
f();
}

fn main() {
let s1 = String::from("hello");
let a = || println!("{}", s1);
apply_fn(a);
apply_fn(a); // 由于引入闭包的是 reference, 多次调用没有问题

let mut s2 = String::from("hello");
let b = || s2.push_str(", world!");
apply_fn_mut(b);

let s3 = String::from("foo");
let c = move || println!("{}", s3);
apply_fn_once(c);
// apply_fn_once(c); // 由于显式地将闭包定义了 `move` 所以强制获取了 s3 的所有权, 多次调用会报错, 符合期待
}