闭包

闭包或 lambda 表达式具有无法命名的类型。不过,它们会 实现特殊的 FnFnMutFnOnce 特征:

  1. fn apply_with_log(func: impl FnOnce(i32) -> i32, input: i32) -> i32 {
  2. println!("Calling function on {input}");
  3. func(input)
  4. }
  5. fn main() {
  6. let add_3 = |x| x + 3;
  7. println!("add_3: {}", apply_with_log(add_3, 10));
  8. println!("add_3: {}", apply_with_log(add_3, 20));
  9. let mut v = Vec::new();
  10. let mut accumulate = |x: i32| {
  11. v.push(x);
  12. v.iter().sum::<i32>()
  13. };
  14. println!("accumulate: {}", apply_with_log(&mut accumulate, 4));
  15. println!("accumulate: {}", apply_with_log(&mut accumulate, 5));
  16. let multiply_sum = |x| x * v.into_iter().sum::<i32>();
  17. println!("multiply_sum: {}", apply_with_log(multiply_sum, 3));
  18. }

This slide should take about 20 minutes.

Fn(例如 add_3)既不会耗用也不会修改捕获的值,或许 也不会捕获任何值。它可被并发调用多次。

FnMut(例如 accumulate)可能会改变捕获的值。您可以多次调用它, 但不能并发调用它。

如果您使用 FnOnce(例如 multiply_sum),或许只能调用它一次。它可能会耗用 所捕获的值。

FnMutFnOnce 的子类型。FnFnMutFnOnce 的子类型。也就是说,您可以在任何 需要调用 FnOnce 的地方使用 FnMut,还可在任何需要调用 FnMutFnOnce 的地方 使用 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 关键字则可让闭包依据值 来捕获数据。

  1. fn make_greeter(prefix: String) -> impl Fn(&str) {
  2. return move |name| println!("{} {}", prefix, name);
  3. }
  4. fn main() {
  5. let hi = make_greeter("Hi".to_string());
  6. hi("Greg");
  7. }