Skip to content

Commit bdd6d22

Browse files
author
Alexis Hunt
committed
Expand on mechanics of closure types.
1 parent 708d8cd commit bdd6d22

File tree

1 file changed

+117
-20
lines changed

1 file changed

+117
-20
lines changed

src/types.md

+117-20
Original file line numberDiff line numberDiff line change
@@ -365,30 +365,99 @@ x = bo(5,7);
365365
## Closure types
366366

367367
A [closure expression] produces a closure value with a unique, anonymous type
368-
that cannot be written out.
368+
that cannot be written out. A closure type is approximately equivalent to a
369+
struct which contains the captured variables. For instance, the following
370+
closure:
369371

370-
Depending on the requirements of the closure, its type implements one or
371-
more of the closure traits:
372+
```rust
373+
fn f<F : FnOnce() -> String> (g: F) {
374+
println!("{}", g());
375+
}
376+
377+
let mut s = String::from("foo");
378+
let t = String::from("bar");
379+
380+
f(|| {
381+
s += t;
382+
s
383+
});
384+
// Prints "foobar".
385+
```
386+
387+
generates a closure type roughly like the following:
388+
389+
```rust
390+
struct Closure<'a> {
391+
s : String
392+
t : &'a String
393+
}
372394

373-
* `FnOnce`
374-
: The closure can be called once. A closure called as `FnOnce` can move out
375-
of its captured values.
395+
impl<'a> FnOnce() -> String for Closure<'a> {
396+
fn call_once(self) -> String {
397+
self.s += self.t;
398+
self.s
399+
}
400+
}
401+
```
376402

377-
* `FnMut`
378-
: The closure can be called multiple times as mutable. A closure called as
379-
`FnMut` can mutate values from its environment. `FnMut` inherits from
380-
`FnOnce` (i.e. anything implementing `FnMut` also implements `FnOnce`).
403+
so that the call to `f` works as if it were:
381404

382-
* `Fn` : The closure can be called multiple times through a shared reference. A
383-
closure called as `Fn` can neither move out from nor mutate captured
384-
variables, but read-only access to such values is allowed. Using `move` to
385-
capture variables by value is allowed so long as they aren't mutated or
386-
moved in the body of the closure. `Fn` inherits from `FnMut`, which itself
387-
inherits from `FnOnce`.
405+
```rust,ignore
406+
f(Closure{s: s, t: &t});
407+
```
388408

389-
Closures that don't use anything from their environment, called *non-capturing
390-
closures*, can be coerced to function pointers (`fn`) with the matching
391-
signature. To adopt the example from the section above:
409+
The compiler prefers to capture a closed-over variable by immutable borrow,
410+
followed by mutable borrow and finally by move (or copy, for [`Copy`] types). It
411+
will pick the first choice of these that allows the closure to compile. If the
412+
`move` keyword is used, then all captures are by move or copy, regardless of
413+
whether a borrow would work. The `move` keyword is usually used to allow the
414+
closure to outlive the captured values, such as if the closure is being returned
415+
or used to spawn a new thread.
416+
417+
Structs and tuples are always captured entirely, not by individual fields. It
418+
may be necessary to borrow into a local variable in order to capture a single
419+
field:
420+
421+
```rust
422+
struct SetVec {
423+
set: HashSet<u32>,
424+
vec: Vec<u32>
425+
}
426+
427+
impl Pair {
428+
fn populate(&mut self) {
429+
let vec = &mut self.vec;
430+
self.set.iter().for_each(|&n| {
431+
vec.push(n);
432+
})
433+
}
434+
}
435+
```
436+
437+
If, instead, the closure were to use `self.vec` directly, then it would attempt
438+
to capture `self` by mutable reference. But since `self.set` is already
439+
borrowed to iterate over, the closure would not compile.
440+
441+
### Call traits and coercions
442+
443+
Closure types all implement `[FnOnce]`, indicating that they can be called once
444+
by consuming ownership of the closure. Additionally, some closures implement
445+
more specific call traits:
446+
447+
* A closure which does not move out of any captured variables implements
448+
`[FnMut]`, indicating that it can be called by mutable reference.
449+
450+
* A closure which does not mutate or move out of any captured variables
451+
implements `[Fn]`, indicating that it can be called by shared reference.
452+
453+
> Note that `move` closures may still implement `[Fn]` or `[FnMut]`, even
454+
> though they capture variables by move. This is because the traits
455+
> implemented by a closure type are determined by what the closure does with
456+
> captured values, not how it captures them.
457+
458+
In addition to the call traits, *non-capturing closures*---those that don't
459+
capture anything from their environment---can be coerced to function pointers
460+
(`fn`) with the matching signature.
392461

393462
```rust
394463
let add = |x, y| x + y;
@@ -400,6 +469,33 @@ let bo: Binop = add;
400469
x = bo(5,7);
401470
```
402471

472+
### Other traits
473+
474+
Closure types implement the following traits, if allowed to do so by the
475+
captured values:
476+
477+
* `[Sized]`
478+
* `[Send]`
479+
* `[Sync]`
480+
* `[Clone]`
481+
* `[Copy]`
482+
483+
`[Sized]` is always implemented (local variables are all sized, so all captured
484+
values must be too). The rules for `[Send]` and `[Sync]` match those for normal
485+
struct types, while `[Clone]` and `[Copy]` behave as if [derived][derive]. For
486+
`[Clone]`, the order of cloning of the captured variables is left unspecified.
487+
488+
Because captures are often by reference, the following general rules arise:
489+
490+
* All closures are `[Sized]`.
491+
* A closure is `[Sync]` if all values captured by mutable reference, move, or
492+
copy are `[Sync]`.
493+
* A closure is `[Send]` if all values captured by shared reference are `[Sync]`,
494+
and all values captured by mutable reference, move, or copy are `[Send]`.
495+
* A closure is `[Clone]` or `[Copy]` if it does not capture any values by
496+
mutable reference, and if all values it captures by move or copy are `[Clone]`
497+
or `[Copy]`, respectively.
498+
403499
## Trait objects
404500

405501
A *trait object* is an opaque value of another type that implements a set of
@@ -593,6 +689,7 @@ impl Printable for String {
593689
[Clone]: special-types-and-traits.html#clone
594690
[Send]: special-types-and-traits.html#send
595691
[Sync]: special-types-and-traits.html#sync
692+
[derive]: attributes.html#derive
596693
[`Vec<T>`]: ../std/vec/struct.Vec.html
597694
[dynamically sized type]: dynamically-sized-types.html
598695
[dynamically sized types]: dynamically-sized-types.html
@@ -603,4 +700,4 @@ impl Printable for String {
603700
[auto traits]: special-types-and-traits.html#auto-traits
604701
[object safe]: items/traits.html#object-safety
605702
[issue 47010]: https://github.com/rust-lang/rust/issues/47010
606-
[issue 33140]: https://github.com/rust-lang/rust/issues/33140
703+
[issue 33140]: https://github.com/rust-lang/rust/issues/33140

0 commit comments

Comments
 (0)