闭包
闭包或 lambda 表达式具有无法命名的类型。不过,它们会 实现特殊的 Fn
, FnMut
和 FnOnce
特征:
fn apply_with_log(func: impl FnOnce(i32) -> i32, input: i32) -> i32 { println!("Calling function on {input}"); func(input) } fn main() { let add_3 = |x| x + 3; println!("add_3: {}", apply_with_log(add_3, 10)); println!("add_3: {}", apply_with_log(add_3, 20)); let mut v = Vec::new(); let mut accumulate = |x: i32| { v.push(x); v.iter().sum::<i32>() }; println!("accumulate: {}", apply_with_log(&mut accumulate, 4)); println!("accumulate: {}", apply_with_log(&mut accumulate, 5)); let multiply_sum = |x| x * v.into_iter().sum::<i32>(); println!("multiply_sum: {}", apply_with_log(multiply_sum, 3)); }
Fn
(例如 add_3
)既不会耗用也不会修改捕获的值,或许 也不会捕获任何值。它可被并发调用多次。
FnMut
(例如 accumulate
)可能会改变捕获的值。您可以多次调用它, 但不能并发调用它。
如果您使用 FnOnce
(例如 multiply_sum
),或许只能调用它一次。它可能会耗用 所捕获的值。
FnMut
是 FnOnce
的子类型。Fn
是 FnMut
和 FnOnce
的子类型。也就是说,您可以在任何 需要调用 FnOnce
的地方使用 FnMut
,还可在任何需要调用 FnMut
或 FnOnce
的地方 使用 Fn
。
When you define a function that takes a closure, you should take FnOnce
if you can (i.e. you call it once), or FnMut
else, and last Fn
. This allows the most flexibility for the caller.
In contrast, when you have a closure, the most flexible you can have is Fn
(it can be passed everywhere), then FnMut
, and lastly FnOnce
.
编译器也会推断 Copy
(例如针对 add_3
)和 Clone
(例如 multiply_sum
), 具体取决于闭包捕获的数据。
默认情况下,闭包会依据引用来捕获数据(如果可以的话)。move
关键字则可让闭包依据值 来捕获数据。
fn make_greeter(prefix: String) -> impl Fn(&str) { return move |name| println!("{} {}", prefix, name); } fn main() { let hi = make_greeter("Hi".to_string()); hi("Greg"); }