Skip to content

Commit

Permalink
Merge pull request rust-lang#249 from alercah/closures
Browse files Browse the repository at this point in the history
Improve documentation on closure types.
  • Loading branch information
Havvy authored Feb 24, 2018
2 parents 72751c5 + 07ccc1d commit ec079d3
Showing 1 changed file with 117 additions and 20 deletions.
137 changes: 117 additions & 20 deletions src/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<F : FnOnce() -> 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<u32>,
vec: Vec<u32>
}

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;
Expand All @@ -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
Expand Down Expand Up @@ -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<T>`]: ../std/vec/struct.Vec.html
[dynamically sized type]: dynamically-sized-types.html
[dynamically sized types]: dynamically-sized-types.html
Expand All @@ -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
[issue 33140]: https://github.com/rust-lang/rust/issues/33140

0 comments on commit ec079d3

Please sign in to comment.