diff --git a/src/types.md b/src/types.md index 60eb470fc..faf896b27 100644 --- a/src/types.md +++ b/src/types.md @@ -365,30 +365,101 @@ x = bo(5,7); ## Closure types A [closure expression] produces a closure value with a unique, anonymous type -that cannot be written out. +that cannot be written out. A closure type is approximately equivalent to a +struct which contains the captured variables. For instance, the following +closure: -Depending on the requirements of the closure, its type implements one or -more of the closure traits: +```rust +fn f String> (g: F) { + println!("{}", g()); +} + +let mut s = String::from("foo"); +let t = String::from("bar"); + +f(|| { + s += &*t; + s +}); +// Prints "foobar". +``` + +generates a closure type roughly like the following: + +```rust,ignore +struct Closure<'a> { + s : String, + t : &'a String, +} -* `FnOnce` - : The closure can be called once. A closure called as `FnOnce` can move out - of its captured values. +impl<'a> (FnOnce() -> String) for Closure<'a> { + fn call_once(self) -> String { + self.s += &*self.t; + self.s + } +} +``` -* `FnMut` - : The closure can be called multiple times as mutable. A closure called as - `FnMut` can mutate values from its environment. `FnMut` inherits from - `FnOnce` (i.e. anything implementing `FnMut` also implements `FnOnce`). +so that the call to `f` works as if it were: -* `Fn` : The closure can be called multiple times through a shared reference. A - closure called as `Fn` can neither move out from nor mutate captured - variables, but read-only access to such values is allowed. Using `move` to - capture variables by value is allowed so long as they aren't mutated or - moved in the body of the closure. `Fn` inherits from `FnMut`, which itself - inherits from `FnOnce`. +```rust,ignore +f(Closure{s: s, t: &t}); +``` -Closures that don't use anything from their environment, called *non-capturing -closures*, can be coerced to function pointers (`fn`) with the matching -signature. To adopt the example from the section above: +The compiler prefers to capture a closed-over variable by immutable borrow, +followed by mutable borrow, by copy, and finally by move. It will pick the first +choice of these that allows the closure to compile. If the `move` keyword is +used, then all captures are by move or copy, regardless of whether a borrow +would work. The `move` keyword is usually used to allow the closure to outlive +the captured values, such as if the closure is being returned or used to spawn a +new thread. + +Composite types such as structs, tuples, and enums are always captured entirely, +not by individual fields. It may be necessary to borrow into a local variable in +order to capture a single field: + +```rust +# use std::collections::HashSet; +# +struct SetVec { + set: HashSet, + vec: Vec +} + +impl SetVec { + fn populate(&mut self) { + let vec = &mut self.vec; + self.set.iter().for_each(|&n| { + vec.push(n); + }) + } +} +``` + +If, instead, the closure were to use `self.vec` directly, then it would attempt +to capture `self` by mutable reference. But since `self.set` is already +borrowed to iterate over, the code would not compile. + +### Call traits and coercions + +Closure types all implement `[FnOnce]`, indicating that they can be called once +by consuming ownership of the closure. Additionally, some closures implement +more specific call traits: + +* A closure which does not move out of any captured variables implements + `[FnMut]`, indicating that it can be called by mutable reference. + +* A closure which does not mutate or move out of any captured variables + implements `[Fn]`, indicating that it can be called by shared reference. + +> Note: `move` closures may still implement `[Fn]` or `[FnMut]`, even though +> they capture variables by move. This is because the traits implemented by a +> closure type are determined by what the closure does with captured values, not +> how it captures them. + +*Non-capturing closures* are closures that don't capture anything from their +environment. They can be coerced to function pointers (`fn`) with the matching +signature. ```rust let add = |x, y| x + y; @@ -400,6 +471,31 @@ let bo: Binop = add; x = bo(5,7); ``` +### Other traits + +All closure types implement `[Sized]`. Additionally, closure types implement the +following traits if allowed to do so by the types of the captures it stores: + +* `[Clone]` +* `[Copy]` +* `[Sync]` +* `[Send]` + +The rules for `[Send]` and `[Sync]` match those for normal struct types, while +`[Clone]` and `[Copy]` behave as if [derived][derive]. For `[Clone]`, the order +of cloning of the captured variables is left unspecified. + +Because captures are often by reference, the following general rules arise: + +* A closure is `[Sync]` if all variables captured by mutable reference, copy, or + move are `[Sync]`. +* A closure is `[Send]` if all variables captured by shared reference are + `[Sync]`, and all values captured by mutable reference, copy, or move are + `[Send]`. +* A closure is `[Clone]` or `[Copy]` if it does not capture any values by + mutable reference, and if all values it captures by copy or move are `[Clone]` + or `[Copy]`, respectively. + ## Trait objects A *trait object* is an opaque value of another type that implements a set of @@ -593,6 +689,7 @@ impl Printable for String { [Clone]: special-types-and-traits.html#clone [Send]: special-types-and-traits.html#send [Sync]: special-types-and-traits.html#sync +[derive]: attributes.html#derive [`Vec`]: ../std/vec/struct.Vec.html [dynamically sized type]: dynamically-sized-types.html [dynamically sized types]: dynamically-sized-types.html @@ -603,4 +700,4 @@ impl Printable for String { [auto traits]: special-types-and-traits.html#auto-traits [object safe]: items/traits.html#object-safety [issue 47010]: /~https://github.com/rust-lang/rust/issues/47010 -[issue 33140]: /~https://github.com/rust-lang/rust/issues/33140 \ No newline at end of file +[issue 33140]: /~https://github.com/rust-lang/rust/issues/33140