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
@@ -98,10 +98,13 @@ Async closures always capture all input arguments, regardless of whether or not
98
98
## Capture precision
99
99
100
100
r[type.closure.capture.precision.capture-path]
101
-
A *capture path* is a sequence starting with a variable from the environment followed by zero or more place projections that were applied to that variable.
101
+
A *capture path* is a sequence starting with a variable from the environment followed by zero or more place projections from that variable.
A *place projection* is a [field access], [tuple index], [dereference] (and automatic dereferences), or [array or slice index] expression applied to a variable.
104
+
A *place projection* is a [field access], [tuple index], [dereference] (and automatic dereferences), [array or slice index] expression, or [pattern destructuring] applied to a variable.
105
+
106
+
> [!NOTE]
107
+
> In `rustc`, pattern destructuring desugars into a series of dereferences and field or element accesses.
105
108
106
109
r[type.closure.capture.precision.intro]
107
110
The closure borrows or moves the capture path, which may be truncated based on the rules described below.
@@ -124,14 +127,15 @@ Here the capture path is the local variable `s`, followed by a field access `.f1
124
127
This closure captures an immutable borrow of `s.f1.1`.
[array or slice index]: ../expressions/array-expr.md#array-and-slice-indexing-expressions
130
134
131
135
r[type.closure.capture.precision.shared-prefix]
132
136
### Shared prefix
133
137
134
-
In the case where a capture 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:
138
+
In the case where a capture path and one of the ancestors 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:
Destructuring tuples, structs, and single-variant enums does not, by itself, cause a read or the place to be captured.
198
210
199
-
letc=||matchx { // x is not captured
200
-
_=>println!("Hello World!")
211
+
> [!NOTE]
212
+
> Enums marked with [`#[non_exhaustive]`][attributes.type-system.non_exhaustive] from other crates are always treated as having multiple variants. See *[type.closure.capture.precision.discriminants.non_exhaustive]*.
213
+
214
+
```rust,no_run
215
+
struct S; // A non-`Copy` type.
216
+
217
+
// Destructuring tuples does not cause a read or capture.
218
+
let x = (S,);
219
+
let c = || {
220
+
let (..) = x; // Does not capture `x`.
201
221
};
222
+
x; // OK: `x` can be moved here.
202
223
c();
203
-
```
204
224
205
-
This also includes destructuring of tuples, structs, and enums.
206
-
Fields matched with the [RestPattern] or [StructPatternEtCetera] are also not considered as read, and thus those fields will not be captured.
207
-
The following illustrates some of these:
225
+
// Destructuring unit structs does not cause a read or capture.
226
+
let x = S;
227
+
let c = || {
228
+
let S = x; // Does not capture `x`.
229
+
};
230
+
x; // OK: `x` can be moved here.
231
+
c();
208
232
209
-
```rust
210
-
letx= (String::from("a"), String::from("b"));
233
+
// Destructuring structs does not cause a read or capture.
234
+
struct W<T>(T);
235
+
let x = W(S);
236
+
let c = || {
237
+
let W(..) = x; // Does not capture `x`.
238
+
};
239
+
x; // OK: `x` can be moved here.
240
+
c();
241
+
242
+
// Destructuring single-variant enums does not cause a read
243
+
// or capture.
244
+
enum E<T> { V(T) }
245
+
let x = E::V(S);
211
246
let c = || {
212
-
let(first, ..) =x; //captures `x.0` ByValue
247
+
let E::V(..) = x; // Does not capture `x`.
213
248
};
214
-
// The first tuple field has been moved into the closure.
215
-
// The second tuple field is still accessible.
216
-
println!("{:?}", x.1);
249
+
x; // OK: `x` can be moved here.
217
250
c();
218
251
```
219
252
220
-
```rust
221
-
structExample {
222
-
f1:String,
223
-
f2:String,
224
-
}
253
+
r[type.closure.capture.precision.wildcard.fields]
254
+
Fields matched against [RestPattern] (`..`) or [StructPatternEtCetera] (also `..`) are not read, and those fields are not captured.
Partial captures of arrays and slices are not supported; the entire slice or array is always captured even if used with wildcard pattern matching, indexing, or sub-slicing.
241
-
For example:
242
269
243
270
```rust,compile_fail,E0382
244
-
#[derive(Debug)]
245
-
struct Example;
246
-
let x = [Example, Example];
247
-
271
+
struct S; // A non-`Copy` type.
272
+
let mut x = [S, S];
248
273
let c = || {
249
-
let [first, _] = x; // captures all of `x` ByValue
274
+
let [x0, _] = x; // Captures all of `x` by `ByValue`.
250
275
};
251
-
c();
252
-
println!("{:?}", x[1]); // ERROR: borrow of moved value: `x`
276
+
let _ = &mut x[1]; // ERROR: Borrow of moved value.
If [`#[non_exhaustive]`][attributes.type-system.non_exhaustive] is applied to an enum defined in an external crate, the enum is treated as having multiple variants for the purpose of deciding whether a read occurs, even if it actually has only one variant.
Even if all variants but the one being matched against are uninhabited, making the pattern [irrefutable][patterns.refutable], the discriminant is still read if it otherwise would be.
348
+
349
+
```rust,compile_fail,E0502
350
+
enum Empty {}
351
+
let mut x = Ok::<_, Empty>(42);
352
+
let c = || {
353
+
let Ok(_) = x; // Captures `x` by `ImmBorrow`.
354
+
};
355
+
let _ = &mut x; // ERROR: Cannot borrow `x` as mutable.
Matching against a [range pattern][patterns.range] reads the place being matched, even if the range includes all possible values of the type, and captures the place by `ImmBorrow`.
365
+
366
+
```rust,compile_fail,E0502
367
+
let mut x = 0u8;
368
+
let c = || {
369
+
let 0..=u8::MAX = x; // Captures `x` by `ImmBorrow`.
370
+
};
371
+
let _ = &mut x; // ERROR: Cannot borrow `x` as mutable.
Matching a slice against a [slice pattern][patterns.slice] other than one with only a single [rest pattern][patterns.rest] (i.e. `[..]`) is treated as a read of the length from the slice and captures the slice by `ImmBorrow`.
380
+
381
+
```rust,compile_fail,E0502
382
+
let x: &mut [u8] = &mut [];
383
+
let c = || match x { // Captures `*x` by `ImmBorrow`.
384
+
&mut [] => (),
385
+
// ^^
386
+
// This matches a slice of exactly zero elements. To know whether the
387
+
// scrutinee matches, the length must be read, causing the slice to
388
+
// be captured.
389
+
_ => (),
390
+
};
391
+
let _ = &mut *x; // ERROR: Cannot borrow `*x` as mutable.
392
+
c();
393
+
```
394
+
395
+
```rust,no_run
396
+
let x: &mut [u8] = &mut [];
397
+
let c = || match x { // Does not capture `*x`.
398
+
[..] => (),
399
+
// ^^ Rest pattern.
400
+
};
401
+
let _ = &mut *x; // OK: `*x` can be borrow here.
402
+
c();
403
+
```
404
+
405
+
> [!NOTE]
406
+
> Perhaps surprisingly, even though the length is contained in the (wide) *pointer* to the slice, it is the place of the *pointee* (the slice) that is treated as read and is captured.
0 commit comments