Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve documentation on closure types. #249

Merged
merged 4 commits into from
Feb 24, 2018
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 119 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 and finally by move (or copy, for [`Copy`] types). It
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might read better if the list was four long and by copy was in there before by move.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need a comma before the and.

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.

Structs and tuples are always captured entirely, not by individual fields. It
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Composite types, such as structs or tuples, are"? Or are enums somehow different?

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 closure would not compile.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 that `move` closures may still implement `[Fn]` or `[FnMut]`, even
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note needs to have a colon after it to remain consistent with the rest of the notes in the reference.

> 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.

In addition to the call traits, *non-capturing closures*---those that don't
capture anything from their environment---can be coerced to function pointers
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Define the term in its own sentence, and then describe how it's supposed to be used.

*Non-capturing closures* are closures that don't capture any variables from their environment. They can be coereced...

(`fn`) with the matching signature.

```rust
let add = |x, y| x + y;
Expand All @@ -400,6 +471,33 @@ let bo: Binop = add;
x = bo(5,7);
```

### Other traits

Closure types implement the following traits, if allowed to do so by the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comma is invalid.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The actual values captured don't matter, but the types of the variables do. I'd personally write it "Closures implement the following traits conditionally on the types of the captured variables."

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a fair point. But it also doesn't necessarily depend on the type of the variable captured, but the type of the capturing variable in the closure. For instance, capturing a non-Copy type by reference is fine for Copy, since the reference is itself Copy.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found a way to make this work, please take a look once I push.

captured values:

* `[Sized]`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels weird that we're talking about conditionally implemented traits and then the first one on the list is always implemented.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed it.

* `[Send]`
* `[Sync]`
* `[Clone]`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given how Clone and Copy are lang items and Send and Sync aren't (I think? Maybe one of them is?), I'd rather see Clone and Copy listed first.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sync is, Send isn't. I think it would look better reordered though, so I will do that.

* `[Copy]`

`[Sized]` is always implemented (local variables are all sized, so all captured
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More of a meta-point, but I really don't like having parentheticals like this. They should generally just be another sentence.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed that sentence anyway.

values must be too). 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:

* All closures are `[Sized]`.
* A closure is `[Sync]` if all values captured by mutable reference, move, or
copy are `[Sync]`.
* A closure is `[Send]` if all values captured by shared reference are `[Sync]`,
and all values captured by mutable reference, move, or copy 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 move or copy 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 +691,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 +702,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