Skip to content

Commit 5b058d5

Browse files
roxeloehuss
authored andcommitted
Update closure types documentation so it includes information about RFC2229
1 parent aa99472 commit 5b058d5

File tree

1 file changed

+254
-47
lines changed

1 file changed

+254
-47
lines changed

src/types/closure.md

Lines changed: 254 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -2,38 +2,46 @@
22

33
A [closure expression] produces a closure value with a unique, anonymous type
44
that cannot be written out. A closure type is approximately equivalent to a
5-
struct which contains the captured variables. For instance, the following
5+
struct which contains the captured values. For instance, the following
66
closure:
77

88
```rust
9+
#[derive(Debug)]
10+
struct Point { x:i32, y:i32 }
11+
struct Rectangle { left_top: Point, right_bottom: Point }
12+
913
fn f<F : FnOnce() -> String> (g: F) {
1014
println!("{}", g());
1115
}
1216

13-
let mut s = String::from("foo");
14-
let t = String::from("bar");
15-
16-
f(|| {
17-
s += &t;
18-
s
19-
});
20-
// Prints "foobar".
17+
let mut rect = Rectangle {
18+
left_top: Point { x: 1, y: 1 },
19+
right_bottom: Point { x: 0, y: 0 }
20+
};
21+
22+
let c = || {
23+
rect.left_top.x += 1;
24+
rect.right_bottom.x += 1;
25+
format!("{:?}", rect.left_top)
26+
};
27+
// Prints "Point { x: 2, y: 1 }".
2128
```
2229

2330
generates a closure type roughly like the following:
2431

25-
<!-- ignore: simplified, requires unboxed_closures, fn_traits -->
32+
<!-- ignore: simplified -->
2633
```rust,ignore
2734
struct Closure<'a> {
28-
s : String,
29-
t : &'a String,
35+
left_top : &'a mut Point,
36+
right_bottom_x : &'a mut i32,
3037
}
3138
3239
impl<'a> FnOnce<()> for Closure<'a> {
3340
type Output = String;
3441
fn call_once(self) -> String {
35-
self.s += &*self.t;
36-
self.s
42+
self.left_top.x += 1;
43+
self.right_bottom_x += 1;
44+
format!("{:?}", self.left_top)
3745
}
3846
}
3947
```
@@ -42,48 +50,150 @@ so that the call to `f` works as if it were:
4250

4351
<!-- ignore: continuation of above -->
4452
```rust,ignore
45-
f(Closure{s: s, t: &t});
53+
f(Closure{ left_top: rect.left_top, right_bottom_x: rect.left_top.x });
4654
```
4755

4856
## Capture modes
4957

50-
The compiler prefers to capture a closed-over variable by immutable borrow,
58+
The compiler prefers to capture a value by immutable borrow,
5159
followed by unique immutable borrow (see below), by mutable borrow, and finally
52-
by move. It will pick the first choice of these that is compatible with how the
53-
captured variable is used inside the closure body. The compiler does not take
54-
surrounding code into account, such as the lifetimes of involved variables, or
55-
of the closure itself.
60+
by move. It will pick the first choice of these that allows the closure to
61+
compile. The choice is made only with regards to the contents of the closure
62+
expression; the compiler does not take into account surrounding code, such as
63+
the lifetimes of involved variables or fields.
64+
>>>>>>> 881f305... Update closure types documentation so it includes information about RFC2229
5665
57-
If the `move` keyword is used, then all captures are by move or, for `Copy`
58-
types, by copy, regardless of whether a borrow would work. The `move` keyword is
59-
usually used to allow the closure to outlive the captured values, such as if the
60-
closure is being returned or used to spawn a new thread.
66+
## Capture Precision
67+
68+
The precise path that gets captured is typically the full path that is used in the closure, but there are cases where we will only capture a prefix of the path.
69+
70+
71+
### Shared prefix
6172

62-
Composite types such as structs, tuples, and enums are always captured entirely,
63-
not by individual fields. It may be necessary to borrow into a local variable in
64-
order to capture a single field:
73+
In the case where a path and one of the ancestor’s of that path are both captured by a closure, the ancestor path is captured with the highest capture mode among the two captures,`CaptureMode = max(AncestorCaptureMode, DescendantCaptureMode)`, using the strict weak ordering
74+
75+
`ImmBorrow < UniqueImmBorrow < MutBorrow < ByValue`.
76+
77+
Note that this might need to be applied recursively.
78+
79+
```rust=
80+
let s = String::new("S");
81+
let t = (s, String::new("T"));
82+
let mut u = (t, String::new("U"));
83+
84+
let c = || {
85+
println!("{:?}", u); // u captured by ImmBorrow
86+
u.0.truncate(0); // u.0 captured by MutBorrow
87+
move_value(u.0.0); // u.0.0 captured by ByValue
88+
};
89+
```
90+
91+
Overall the closure will capture `u` by `ByValue`.
92+
93+
### Wild Card Patterns
94+
Closures only capture data that needs to be read, which means the following closures will not capture `x`
6595

6696
```rust
67-
# use std::collections::HashSet;
68-
#
69-
struct SetVec {
70-
set: HashSet<u32>,
71-
vec: Vec<u32>
72-
}
97+
let x = 10;
98+
let c = || {
99+
let _ = x;
100+
};
101+
102+
let c = || match x {
103+
_ => println!("Hello World!")
104+
};
105+
```
73106

74-
impl SetVec {
75-
fn populate(&mut self) {
76-
let vec = &mut self.vec;
77-
self.set.iter().for_each(|&n| {
78-
vec.push(n);
79-
})
80-
}
81-
}
107+
### Capturing references in move contexts
108+
109+
Rust doesn't allow moving fields out of references. As a result, in the case of move closures, when values accessed through a shared references are moved into the closure body, the compiler, instead of moving the values out of the reference, would reborrow the data.
110+
111+
```rust
112+
struct T(String, String);
113+
114+
let mut t = T(String::from("foo"), String::from("bar"));
115+
let t = &mut t;
116+
let c = move || t.0.truncate(0); // closure captures (&mut t.0)
82117
```
83118

84-
If, instead, the closure were to use `self.vec` directly, then it would attempt
85-
to capture `self` by mutable reference. But since `self.set` is already
86-
borrowed to iterate over, the code would not compile.
119+
### Raw pointer dereference
120+
In Rust, it's `unsafe` to dereference a raw pointer. Therefore, closures will only capture the prefix of a path that runs up to, but not including, the first dereference of a raw pointer.
121+
122+
```rust,
123+
struct T(String, String);
124+
125+
let t = T(String::from("foo"), String::from("bar"));
126+
let t = &t as *const T;
127+
128+
let c = || unsafe {
129+
println!("{}", (*t).0); // closure captures t
130+
};
131+
```
132+
133+
### Reference into unaligned `struct`s
134+
135+
In Rust, it's `unsafe` to hold references to unaligned fields in a structure, and therefore, closures will only capture the prefix of the path that runs up to, but not including, the first field access into an unaligned structure.
136+
137+
```rust
138+
#[repr(packed)]
139+
struct T(String, String);
140+
141+
let t = T(String::from("foo"), String::from("bar"));
142+
let c = || unsafe {
143+
println!("{}", t.0); // closure captures t
144+
};
145+
```
146+
147+
148+
### `Box` vs other `Deref` implementations
149+
150+
The compiler treats the implementation of the Deref trait for `Box` differently, as it is considered a special entity.
151+
152+
For example, let us look at examples involving `Rc` and `Box`. The `*rc` is desugared to a call to the trait method `deref` defined on `Rc`, but since `*box` is treated differently by the compiler, the compiler is able to do precise capture on contents of the `Box`.
153+
154+
#### Non `move` closure
155+
156+
In a non `move` closure, if the contents of the `Box` are not moved into the closure body, the contents of the `Box` are precisely captured.
157+
158+
```rust
159+
# use std::rc::Rc;
160+
161+
struct S(i32);
162+
163+
let b = Box::new(S(10));
164+
let c_box = || {
165+
println!("{}", (*b).0); // closure captures `(*b).0`
166+
};
167+
168+
let r = Rc::new(S(10));
169+
let c_rc = || {
170+
println!("{}", (*r).0); // closure caprures `r`
171+
};
172+
```
173+
174+
However, if the contents of the `Box` are moved into the closure, then the box is entirely captured. This is done so the amount of data that needs to be moved into the closure is minimized.
175+
176+
```rust
177+
struct S(i32);
178+
179+
let b = Box::new(S(10));
180+
let c_box = || {
181+
let x = (*b).0; // closure captures `b`
182+
};
183+
```
184+
185+
#### `move` closure
186+
187+
Similarly to moving contents of a `Box` in a non-`move` closure, reading the contents of a `Box` in a `move` closure will capture the `Box` entirely.
188+
189+
```rust
190+
struct S(i32);
191+
192+
let b = Box::new(S(10));
193+
let c_box = || {
194+
println!("{}", (*b).0); // closure captures `b`
195+
};
196+
```
87197

88198
## Unique immutable borrows in captures
89199

@@ -113,6 +223,7 @@ the declaration of `y` will produce an error because it would violate the
113223
uniqueness of the closure's borrow of `x`; the declaration of z is valid because
114224
the closure's lifetime has expired at the end of the block, releasing the borrow.
115225

226+
116227
## Call traits and coercions
117228

118229
Closure types all implement [`FnOnce`], indicating that they can be called once
@@ -156,12 +267,13 @@ following traits if allowed to do so by the types of the captures it stores:
156267

157268
The rules for [`Send`] and [`Sync`] match those for normal struct types, while
158269
[`Clone`] and [`Copy`] behave as if [derived]. For [`Clone`], the order of
159-
cloning of the captured variables is left unspecified.
270+
cloning of the captured values is left unspecified.
271+
160272

161273
Because captures are often by reference, the following general rules arise:
162274

163-
* A closure is [`Sync`] if all captured variables are [`Sync`].
164-
* A closure is [`Send`] if all variables captured by non-unique immutable
275+
* A closure is [`Sync`] if all captured values are [`Sync`].
276+
* A closure is [`Send`] if all values captured by non-unique immutable
165277
reference are [`Sync`], and all values captured by unique immutable or mutable
166278
reference, copy, or move are [`Send`].
167279
* A closure is [`Clone`] or [`Copy`] if it does not capture any values by
@@ -178,3 +290,98 @@ Because captures are often by reference, the following general rules arise:
178290
[`Sync`]: ../special-types-and-traits.md#sync
179291
[closure expression]: ../expressions/closure-expr.md
180292
[derived]: ../attributes/derive.md
293+
294+
## Drop Order
295+
296+
If a closure captures a field of a composite types such as structs, tuples, and enums by value, the field's lifetime would now be tied to the closure. As a result, it is possible for disjoint fields of a composite types to be dropped at different times.
297+
298+
```rust
299+
{
300+
let tuple =
301+
(String::from("foo"), String::from("bar")); // --+
302+
{ // |
303+
let c = || { // ----------------------------+ |
304+
// tuple.0 is captured into the closure | |
305+
drop(tuple.0); // | |
306+
}; // | |
307+
} // 'c' and 'tuple.0' dropped here ------------+ |
308+
} // tuple.1 dropped here -----------------------------+
309+
```
310+
311+
# Edition 2018 and before
312+
313+
## Closure types difference
314+
315+
In Edition 2018 and before, a closure would capture variables in its entirety. This means that for the example used in the [Closure types](#closure-types) section, the generated closure type would instead look something like this:
316+
317+
<!-- ignore: simplified -->
318+
```rust,ignore
319+
struct Closure<'a> {
320+
rect : &'a mut Rectangle,
321+
}
322+
323+
impl<'a> FnOnce<()> for Closure<'a> {
324+
type Output = String;
325+
fn call_once(self) -> String {
326+
self.rect.left_top.x += 1;
327+
self.rect.right_bottom.x += 1;
328+
format!("{:?}", self.rect.left_top)
329+
}
330+
}
331+
```
332+
and the call to `f` would work as follows:
333+
<!-- ignore: continuation of above -->
334+
```rust,ignore
335+
f(Closure { rect: rect });
336+
```
337+
338+
## Capture precision difference
339+
340+
Composite types such as structs, tuples, and enums are always captured in its intirety,
341+
not by individual fields. As a result, it may be necessary to borrow into a local variable in order to capture a single field:
342+
343+
```rust
344+
# use std::collections::HashSet;
345+
#
346+
struct SetVec {
347+
set: HashSet<u32>,
348+
vec: Vec<u32>
349+
}
350+
351+
impl SetVec {
352+
fn populate(&mut self) {
353+
let vec = &mut self.vec;
354+
self.set.iter().for_each(|&n| {
355+
vec.push(n);
356+
})
357+
}
358+
}
359+
```
360+
361+
If, instead, the closure were to use `self.vec` directly, then it would attempt
362+
to capture `self` by mutable reference. But since `self.set` is already
363+
borrowed to iterate over, the code would not compile.
364+
365+
If the `move` keyword is used, then all captures are by move or, for `Copy`
366+
types, by copy, regardless of whether a borrow would work. The `move` keyword is
367+
usually used to allow the closure to outlive the captured values, such as if the
368+
closure is being returned or used to spawn a new thread.
369+
370+
Regardless of if the data will be read by the closure, i.e. in case of wild card patterns, if a variable defined outside the closure is mentioned within the closure the variable will be captured in its entirety.
371+
372+
## Drop order difference
373+
374+
As composite types are captured in their entirety, a closure which captures one of those composite types by value would drop the entire captured variable at the same time as the closure gets dropped.
375+
376+
```rust
377+
{
378+
let tuple =
379+
(String::from("foo"), String::from("bar"));
380+
{
381+
let c = || { // --------------------------+
382+
// tuple is captured into the closure |
383+
drop(tuple.0); // |
384+
}; // |
385+
} // 'c' and 'tuple' dropped here ------------+
386+
}
387+
```

0 commit comments

Comments
 (0)