Skip to content

Commit ab722ef

Browse files
committed
Document how closure capturing interacts with discriminant reads
This is the behavior after the bugfixes in rustc PR 138961.
1 parent 118fd1f commit ab722ef

File tree

1 file changed

+68
-2
lines changed

1 file changed

+68
-2
lines changed

src/types/closure.md

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,10 @@ Async closures always capture all input arguments, regardless of whether or not
9898
## Capture Precision
9999

100100
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 that were applied to that variable, as well as
102+
any [further projections performed by matching against patterns][pattern-wildcards].
103+
104+
[pattern-wildcards]: type.closure.capture.precision.wildcard
102105

103106
r[type.closure.capture.precision.place-projection]
104107
A *place projection* is a [field access], [tuple index], [dereference] (and automatic dereferences), or [array or slice index] expression applied to a variable.
@@ -202,7 +205,7 @@ let c = || match x { // x is not captured
202205
c();
203206
```
204207

205-
This also includes destructuring of tuples, structs, and enums.
208+
This also includes destructuring of tuples, structs, and single-variant enums.
206209
Fields matched with the [RestPattern] or [StructPatternEtCetera] are also not considered as read, and thus those fields will not be captured.
207210
The following illustrates some of these:
208211

@@ -264,6 +267,69 @@ let c = || {
264267

265268
[wildcard pattern]: ../patterns.md#wildcard-pattern
266269

270+
r[type.closure.capture.precision.discriminants]
271+
### Capturing for discriminant reads
272+
273+
If pattern matching requires inspecting a discriminant, the relevant place will get captured by `ImmBorrow`.
274+
275+
```rust
276+
enum Example {
277+
A(i32),
278+
B(i32),
279+
}
280+
281+
let mut x = (Example::A(21), 37);
282+
283+
let c = || match x { // captures `x.0` by ImmBorrow
284+
(Example::A(_), _) => println!("variant A"),
285+
(Example::B(_), _) => println!("variant B"),
286+
};
287+
x.1 += 1; // x.1 can still be modified
288+
c();
289+
```
290+
291+
r[type.closure.capture.precision.discriminants.single-variant]
292+
Matching against the only variant of an enum does not constitute a discriminant read.
293+
294+
```rust
295+
enum Example {
296+
A(i32),
297+
}
298+
299+
let mut x = Example::A(42);
300+
let c = || {
301+
let Example::A(_) = x; // does not capture `x`
302+
};
303+
x = Example::A(57); // x can be modified while the closure is live
304+
c();
305+
```
306+
307+
r[type.closure.capture.precision.discriminants.non-exhaustive]
308+
If [the `#[non_exhaustive]` attribute][non_exhaustive] is applied to an enum
309+
defined in an external crate, it is considered to have multiple variants,
310+
even if only one variant is actually present.
311+
312+
[non_exhaustive]: attributes.type-system.non_exhaustive
313+
314+
r[type.closure.capture.precision.discriminants.uninhabited-variant]
315+
Even if all other variants are uninhabited, the discriminant read still occurs.
316+
317+
```rust,compile_fail,E0506
318+
enum Void {}
319+
320+
enum Example {
321+
A(i32),
322+
B(Void),
323+
}
324+
325+
let mut x = Example::A(42);
326+
let c = || {
327+
let Example::A(_) = x; // captures `x` by ImmBorrow
328+
};
329+
x = Example::A(57); // ERROR: cannot assign to `x` because it is borrowed
330+
c();
331+
```
332+
267333
r[type.closure.capture.precision.move-dereference]
268334
### Capturing references in move contexts
269335

0 commit comments

Comments
 (0)