Capturing

Closures are inherently flexible and will do what the functionality requires
to make the closure work without annotation. This allows capturing to
flexibly adapt to the use case, sometimes moving and sometimes borrowing.
Closures can capture variables:

  • by reference: &T
  • by mutable reference: &mut T
  • by value: T

They preferentially capture variables by reference and only go lower when
required.

  1. fn main() {
  2. use std::mem;
  3. let color = "green";
  4. // A closure to print `color` which immediately borrows (`&`)
  5. // `color` and stores the borrow and closure in the `print`
  6. // variable. It will remain borrowed until `print` goes out of
  7. // scope. `println!` only requires `by reference` so it doesn't
  8. // impose anything more restrictive.
  9. let print = || println!("`color`: {}", color);
  10. // Call the closure using the borrow.
  11. print();
  12. print();
  13. let mut count = 0;
  14. // A closure to increment `count` could take either `&mut count`
  15. // or `count` but `&mut count` is less restrictive so it takes
  16. // that. Immediately borrows `count`.
  17. //
  18. // A `mut` is required on `inc` because a `&mut` is stored inside.
  19. // Thus, calling the closure mutates the closure which requires
  20. // a `mut`.
  21. let mut inc = || {
  22. count += 1;
  23. println!("`count`: {}", count);
  24. };
  25. // Call the closure.
  26. inc();
  27. inc();
  28. //let reborrow = &mut count;
  29. // ^ TODO: try uncommenting this line.
  30. // A non-copy type.
  31. let movable = Box::new(3);
  32. // `mem::drop` requires `T` so this must take by value. A copy type
  33. // would copy into the closure leaving the original untouched.
  34. // A non-copy must move and so `movable` immediately moves into
  35. // the closure.
  36. let consume = || {
  37. println!("`movable`: {:?}", movable);
  38. mem::drop(movable);
  39. };
  40. // `consume` consumes the variable so this can only be called once.
  41. consume();
  42. //consume();
  43. // ^ TODO: Try uncommenting this line.
  44. }

Using move before vertical pipes forces closure
to take ownership of captured variables:

  1. fn main() {
  2. // `Vec` has non-copy semantics.
  3. let haystack = vec![1, 2, 3];
  4. let contains = move |needle| haystack.contains(needle);
  5. println!("{}", contains(&1));
  6. println!("{}", contains(&4));
  7. // `println!("There're {} elements in vec", haystack.len());`
  8. // ^ Uncommenting above line will result in compile-time error
  9. // because borrow checker doesn't allow re-using variable after it
  10. // has been moved.
  11. // Removing `move` from closure's signature will cause closure
  12. // to borrow _haystack_ variable immutably, hence _haystack_ is still
  13. // available and uncommenting above line will not cause an error.
  14. }

See also:

Box and std::mem::drop