summaryrefslogtreecommitdiffstats
path: root/src/doc/rust-by-example/src/fn/closures/capture.md
diff options
context:
space:
mode:
Diffstat (limited to 'src/doc/rust-by-example/src/fn/closures/capture.md')
-rw-r--r--src/doc/rust-by-example/src/fn/closures/capture.md115
1 files changed, 115 insertions, 0 deletions
diff --git a/src/doc/rust-by-example/src/fn/closures/capture.md b/src/doc/rust-by-example/src/fn/closures/capture.md
new file mode 100644
index 000000000..061ef1c7b
--- /dev/null
+++ b/src/doc/rust-by-example/src/fn/closures/capture.md
@@ -0,0 +1,115 @@
+# 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.
+
+```rust,editable
+fn main() {
+ use std::mem;
+
+ let color = String::from("green");
+
+ // A closure to print `color` which immediately borrows (`&`) `color` and
+ // stores the borrow and closure in the `print` variable. It will remain
+ // borrowed until `print` is used the last time.
+ //
+ // `println!` only requires arguments by immutable reference so it doesn't
+ // impose anything more restrictive.
+ let print = || println!("`color`: {}", color);
+
+ // Call the closure using the borrow.
+ print();
+
+ // `color` can be borrowed immutably again, because the closure only holds
+ // an immutable reference to `color`.
+ let _reborrow = &color;
+ print();
+
+ // A move or reborrow is allowed after the final use of `print`
+ let _color_moved = color;
+
+
+ let mut count = 0;
+ // A closure to increment `count` could take either `&mut count` or `count`
+ // but `&mut count` is less restrictive so it takes that. Immediately
+ // borrows `count`.
+ //
+ // A `mut` is required on `inc` because a `&mut` is stored inside. Thus,
+ // calling the closure mutates the closure which requires a `mut`.
+ let mut inc = || {
+ count += 1;
+ println!("`count`: {}", count);
+ };
+
+ // Call the closure using a mutable borrow.
+ inc();
+
+ // The closure still mutably borrows `count` because it is called later.
+ // An attempt to reborrow will lead to an error.
+ // let _reborrow = &count;
+ // ^ TODO: try uncommenting this line.
+ inc();
+
+ // The closure no longer needs to borrow `&mut count`. Therefore, it is
+ // possible to reborrow without an error
+ let _count_reborrowed = &mut count;
+
+
+ // A non-copy type.
+ let movable = Box::new(3);
+
+ // `mem::drop` requires `T` so this must take by value. A copy type
+ // would copy into the closure leaving the original untouched.
+ // A non-copy must move and so `movable` immediately moves into
+ // the closure.
+ let consume = || {
+ println!("`movable`: {:?}", movable);
+ mem::drop(movable);
+ };
+
+ // `consume` consumes the variable so this can only be called once.
+ consume();
+ // consume();
+ // ^ TODO: Try uncommenting this line.
+}
+```
+
+Using `move` before vertical pipes forces closure
+to take ownership of captured variables:
+
+```rust,editable
+fn main() {
+ // `Vec` has non-copy semantics.
+ let haystack = vec![1, 2, 3];
+
+ let contains = move |needle| haystack.contains(needle);
+
+ println!("{}", contains(&1));
+ println!("{}", contains(&4));
+
+ // println!("There're {} elements in vec", haystack.len());
+ // ^ Uncommenting above line will result in compile-time error
+ // because borrow checker doesn't allow re-using variable after it
+ // has been moved.
+
+ // Removing `move` from closure's signature will cause closure
+ // to borrow _haystack_ variable immutably, hence _haystack_ is still
+ // available and uncommenting above line will not cause an error.
+}
+```
+
+### See also:
+
+[`Box`][box] and [`std::mem::drop`][drop]
+
+[box]: ../../std/box.md
+[drop]: https://doc.rust-lang.org/std/mem/fn.drop.html