作为输入参数
虽然 Rust 大多会在不使用类型注解的情况下动态选择如何捕获变量,但在编写函数时不允许这种模糊性。当把闭包作为输入参数时,闭包的完整类型必须使用一些 trait
进行注解,并且它们由闭包如何处理捕获的值来决定。按限制递减的顺序,它们是
Fn
:闭包通过引用 (&T
) 使用捕获的值FnMut
:闭包通过可变引用 (&mut T
) 使用捕获的值FnOnce
:闭包通过值 (T
) 使用捕获的值
在逐个变量的基础上,编译器将以尽可能最不限制的方式捕获变量。
例如,考虑一个被注解为 FnOnce
的参数。这指定闭包 *可能* 通过 &T
、&mut T
或 T
捕获,但编译器最终将根据捕获的变量在闭包中的使用方式进行选择。
这是因为如果可以移动,那么任何类型的借用也应该是可能的。请注意,反之则不然。如果参数被注解为 Fn
,则不允许通过 &mut T
或 T
捕获变量。但是,允许使用 &T
。
在下面的例子中,尝试交换 Fn
、FnMut
和 FnOnce
的用法,看看会发生什么
// A function which takes a closure as an argument and calls it. // <F> denotes that F is a "Generic type parameter" fn apply<F>(f: F) where // The closure takes no input and returns nothing. F: FnOnce() { // ^ TODO: Try changing this to `Fn` or `FnMut`. f(); } // A function which takes a closure and returns an `i32`. fn apply_to_3<F>(f: F) -> i32 where // The closure takes an `i32` and returns an `i32`. F: Fn(i32) -> i32 { f(3) } fn main() { use std::mem; let greeting = "hello"; // A non-copy type. // `to_owned` creates owned data from borrowed one 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); }; // Call the function which applies the closure. apply(diary); // `double` satisfies `apply_to_3`'s trait bound let double = |x| 2 * x; println!("3 doubled: {}", apply_to_3(double)); }