-
Notifications
You must be signed in to change notification settings - Fork 504
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
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Need a comma before the |
||
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
|
||
(`fn`) with the matching signature. | ||
|
||
```rust | ||
let add = |x, y| x + y; | ||
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The comma is invalid. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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." There was a problem hiding this comment. Choose a reason for hiding this commentThe 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- There was a problem hiding this comment. Choose a reason for hiding this commentThe 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]` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed it. |
||
* `[Send]` | ||
* `[Sync]` | ||
* `[Clone]` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
* `[Copy]` | ||
|
||
`[Sized]` is always implemented (local variables are all sized, so all captured | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
|
@@ -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 | ||
|
@@ -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 |
There was a problem hiding this comment.
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 beforeby move
.