You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The compiler prefers to capture a closed-over variable by immutable borrow,
58
+
The compiler prefers to capture a value by immutable borrow,
51
59
followed by unique immutable borrow (see below), by mutable borrow, and finally
52
-
by move. It will pick the first choice of these that allows the closure to
60
+
by value (move or copy). It will pick the first choice of these that allows the closure to
53
61
compile. The choice is made only with regards to the contents of the closure
54
62
expression; the compiler does not take into account surrounding code, such as
55
-
the lifetimes of involved variables.
63
+
the lifetimes of involved variables or fields.
56
64
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.
65
+
## Capture Precision
66
+
67
+
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.
68
+
69
+
70
+
### Shared prefix
61
71
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:
72
+
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
Note that this might need to be applied recursively.
77
+
78
+
```rust=
79
+
let s = String::new("S");
80
+
let t = (s, String::new("T"));
81
+
let mut u = (t, String::new("U"));
82
+
83
+
let c = || {
84
+
println!("{:?}", u); // u captured by ImmBorrow
85
+
u.0.truncate(0); // u.0 captured by MutBorrow
86
+
move_value(u.0.0); // u.0.0 captured by ByValue
87
+
};
88
+
```
89
+
90
+
Overall the closure will capture `u` by `ByValue`.
91
+
92
+
### Wild Card Patterns
93
+
Closures only capture data that needs to be read, which means the following closures will not capture `x`
65
94
66
95
```rust
67
-
# usestd::collections::HashSet;
68
-
#
69
-
structSetVec {
70
-
set:HashSet<u32>,
71
-
vec:Vec<u32>
72
-
}
96
+
letx=10;
97
+
letc=|| {
98
+
let_=x;
99
+
};
100
+
101
+
letc=||matchx {
102
+
_=>println!("Hello World!")
103
+
};
104
+
```
73
105
74
-
implSetVec {
75
-
fnpopulate(&mutself) {
76
-
letvec=&mutself.vec;
77
-
self.set.iter().for_each(|&n| {
78
-
vec.push(n);
79
-
})
80
-
}
81
-
}
106
+
### Capturing references in move contexts
107
+
108
+
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.
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.
118
+
### Raw pointer dereference
119
+
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.
120
+
121
+
```rust,
122
+
struct T(String, String);
123
+
124
+
let t = T(String::from("foo"), String::from("bar"));
125
+
let t = &t as *const T;
126
+
127
+
let c = || unsafe {
128
+
println!("{}", (*t).0); // closure captures t
129
+
};
130
+
```
131
+
132
+
### Reference into unaligned `struct`s
133
+
134
+
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.
135
+
136
+
```rust
137
+
#[repr(packed)]
138
+
structT(String, String);
139
+
140
+
lett=T(String::from("foo"), String::from("bar"));
141
+
letc=||unsafe {
142
+
println!("{}", t.0); // closure captures t
143
+
};
144
+
```
145
+
146
+
147
+
### `Box` vs other `Deref` implementations
148
+
149
+
The compiler treats the implementation of the Deref trait for `Box` differently, as it is considered a special entity.
150
+
151
+
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`.
152
+
153
+
#### Non `move` closure
154
+
155
+
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.
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.
174
+
175
+
```rust
176
+
structS(i32);
177
+
178
+
letb=Box::new(S(10));
179
+
letc_box=|| {
180
+
letx= (*b).0; // closure captures `b`
181
+
};
182
+
```
183
+
184
+
#### `move` closure
185
+
186
+
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.
187
+
188
+
```rust
189
+
structS(i32);
190
+
191
+
letb=Box::new(S(10));
192
+
letc_box=|| {
193
+
println!("{}", (*b).0); // closure captures `b`
194
+
};
195
+
```
87
196
88
197
## Unique immutable borrows in captures
89
198
@@ -113,6 +222,7 @@ the declaration of `y` will produce an error because it would violate the
113
222
uniqueness of the closure's borrow of `x`; the declaration of z is valid because
114
223
the closure's lifetime has expired at the end of the block, releasing the borrow.
115
224
225
+
116
226
## Call traits and coercions
117
227
118
228
Closure types all implement [`FnOnce`], indicating that they can be called once
@@ -156,12 +266,13 @@ following traits if allowed to do so by the types of the captures it stores:
156
266
157
267
The rules for [`Send`] and [`Sync`] match those for normal struct types, while
158
268
[`Clone`] and [`Copy`] behave as if [derived]. For [`Clone`], the order of
159
-
cloning of the captured variables is left unspecified.
269
+
cloning of the captured values is left unspecified.
270
+
160
271
161
272
Because captures are often by reference, the following general rules arise:
162
273
163
-
* A closure is [`Sync`] if all captured variables are [`Sync`].
164
-
* A closure is [`Send`] if all variables captured by non-unique immutable
274
+
* A closure is [`Sync`] if all captured values are [`Sync`].
275
+
* A closure is [`Send`] if all values captured by non-unique immutable
165
276
reference are [`Sync`], and all values captured by unique immutable or mutable
166
277
reference, copy, or move are [`Send`].
167
278
* A closure is [`Clone`] or [`Copy`] if it does not capture any values by
@@ -178,3 +289,98 @@ Because captures are often by reference, the following general rules arise:
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.
} // 'c' and 'tuple.0' dropped here ------------+ |
307
+
} // tuple.1 dropped here -----------------------------+
308
+
```
309
+
310
+
# Edition 2018 and before
311
+
312
+
## Closure types difference
313
+
314
+
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:
315
+
316
+
<!-- ignore: simplified -->
317
+
```rust,ignore
318
+
struct Closure<'a> {
319
+
rect : &'a mut Rectangle,
320
+
}
321
+
322
+
impl<'a> FnOnce<()> for Closure<'a> {
323
+
type Output = String;
324
+
fn call_once(self) -> String {
325
+
self.rect.left_top.x += 1;
326
+
self.rect.right_bottom.x += 1;
327
+
format!("{:?}", self.rect.left_top)
328
+
}
329
+
}
330
+
```
331
+
and the call to `f` would work as follows:
332
+
<!-- ignore: continuation of above -->
333
+
```rust,ignore
334
+
f(Closure { rect: rect });
335
+
```
336
+
337
+
## Capture precision difference
338
+
339
+
Composite types such as structs, tuples, and enums are always captured in its intirety,
340
+
not by individual fields. As a result, it may be necessary to borrow into a local variable in order to capture a single field:
341
+
342
+
```rust
343
+
# usestd::collections::HashSet;
344
+
#
345
+
structSetVec {
346
+
set:HashSet<u32>,
347
+
vec:Vec<u32>
348
+
}
349
+
350
+
implSetVec {
351
+
fnpopulate(&mutself) {
352
+
letvec=&mutself.vec;
353
+
self.set.iter().for_each(|&n| {
354
+
vec.push(n);
355
+
})
356
+
}
357
+
}
358
+
```
359
+
360
+
If, instead, the closure were to use `self.vec` directly, then it would attempt
361
+
to capture `self` by mutable reference. But since `self.set` is already
362
+
borrowed to iterate over, the code would not compile.
363
+
364
+
If the `move` keyword is used, then all captures are by move or, for `Copy`
365
+
types, by copy, regardless of whether a borrow would work. The `move` keyword is
366
+
usually used to allow the closure to outlive the captured values, such as if the
367
+
closure is being returned or used to spawn a new thread.
368
+
369
+
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.
370
+
371
+
## Drop order difference
372
+
373
+
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.
0 commit comments