Skip to content

Commit 32363c9

Browse files
committed
Revise text on temporaries and destructuring assignment
Among other things, within the admonitions, let's pull more of the explanation out of code comments and into the main narrative; let's break up this one admonition into two admonitions, as there are separate ideas here; let's add rule identifiers for these admonitions; and let's reference these admonitions from the relevant places in the destructors chapter.
1 parent 006076a commit 32363c9

File tree

2 files changed

+68
-24
lines changed

2 files changed

+68
-24
lines changed

src/destructors.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,9 @@ smallest scope that contains the expression and is one of the following:
266266
> [!NOTE]
267267
> The [scrutinee] of a `match` expression is not a temporary scope, so temporaries in the scrutinee can be dropped after the `match` expression. For example, the temporary for `1` in `match 1 { ref mut z => z };` lives until the end of the statement.
268268
269+
> [!NOTE]
270+
> The desugaring of a [destructuring assignment] restricts the temporary scope of its assigned value operand (the RHS). For details, see [expr.assign.destructure.tmp-scopes].
271+
269272
r[destructors.scope.temporary.edition2024]
270273
> [!EDITION-2024]
271274
> The 2024 edition added two new temporary scope narrowing rules: `if let` temporaries are dropped before the `else` block, and temporaries of tail expressions of blocks are dropped immediately after the tail expression is evaluated.
@@ -485,6 +488,9 @@ expression which is one of the following:
485488
* The final expression of an extending [`if`] expression's consequent, `else if`, or `else` block.
486489
* An arm expression of an extending [`match`] expression.
487490

491+
> [!NOTE]
492+
> The desugaring of a [destructuring assignment] makes its assigned value operand (the RHS) an extending expression within a newly-introduced block. For details, see [expr.assign.destructure.tmp-ext].
493+
488494
So the borrow expressions in `&mut 0`, `(&1, &mut 2)`, and `Some(&mut 3)`
489495
are all extending expressions. The borrows in `&0 + &1` and `f(&mut 0)` are not.
490496

@@ -640,6 +646,7 @@ There is one additional case to be aware of: when a panic reaches a [non-unwindi
640646
[binding modes]: patterns.md#binding-modes
641647
[closure]: types/closure.md
642648
[destructors]: destructors.md
649+
[destructuring assignment]: expr.assign.destructure
643650
[expression]: expressions.md
644651
[identifier pattern]: patterns.md#identifier-patterns
645652
[initialized]: glossary.md#initialized

src/expressions/operator-expr.md

Lines changed: 61 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -862,48 +862,83 @@ r[expr.assign.destructure.discard-value]
862862
r[expr.assign.destructure.default-binding]
863863
Note that default binding modes do not apply for the desugared expression.
864864

865+
r[expr.assign.destructure.tmp-scopes]
865866
> [!NOTE]
866-
> Although basic assignment expressions are not [temporary scopes], the desugaring of destructuring assignments restricts the temporary scope of its assigned value operand.
867-
> For example:
867+
> The desugaring restricts the [temporary scope] of the assigned value operand (the RHS) of a destructuring assignment.
868+
>
869+
> In a basic assignment, the [temporary] is dropped at the end of the enclosing temporary scope. Below, that's the statement. Therefore, the assignment and use is allowed.
868870
>
869871
> ```rust
870872
> # fn temp() {}
871-
> use std::convert::identity;
872-
>
873+
> fn f<T>(x: T) -> T { x }
873874
> let x;
874-
> // In a basic assignment, `temp()` is dropped at the end of the
875-
> // enclosing temporary scope, so `x` can be assigned and used
876-
> // within the same temporary scope.
877-
> (x = identity(&temp()), x);
875+
> (x = f(&temp()), x); // OK
878876
> ```
879877
>
878+
> Conversely, in a destructuring assignment, the temporary is dropped at the end of the `let` statement in the desugaring. As that happens before we try to assign to `x`, below, it fails.
879+
>
880880
> ```rust,compile_fail,E0716
881881
> # fn temp() {}
882-
> # use std::convert::identity;
883-
> let x;
884-
> // In a destructuring assignment, `temp()` is dropped at the end of
885-
> // the `let` statement in the desugaring, so `x` cannot be assigned.
886-
> [x] = [identity(&temp())]; // ERROR
882+
> # fn f<T>(x: T) -> T { x }
883+
> # let x;
884+
> [x] = [f(&temp())]; // ERROR
887885
> ```
888886
>
889-
> Additionally, [temporary lifetime extension] applies to the `let` statement in a desugared destructuring assignment.
887+
> This desugars to:
888+
>
889+
> ```rust,compile_fail,E0716
890+
> # fn temp() {}
891+
> # fn f<T>(x: T) -> T { x }
892+
> # let x;
893+
> {
894+
> let [_x] = [f(&temp())];
895+
> // ^
896+
> // The temporary is dropped here.
897+
> x = _x; // ERROR
898+
> }
899+
> ```
900+
901+
r[expr.assign.destructure.tmp-ext]
902+
> [!NOTE]
903+
> Due to the desugaring, the assigned value operand (the RHS) of a destructuring assignment is an [extending expression] within a newly-introduced block.
904+
>
905+
> Below, because the [temporary scope] is extended to the end of this introduced block, the assignment is allowed.
890906
>
891907
> ```rust
892908
> # fn temp() {}
893-
> # use std::convert::identity;
894-
> let x;
895-
> // The temporary scope of `temp()` is extended to the end of the
896-
> // block in the desugaring, so `x` may be assigned, but it may not
897-
> // be used.
898-
> [x] = [&temp()];
909+
> # let x;
910+
> [x] = [&temp()]; // OK
899911
> ```
900912
>
913+
> This desugars to:
914+
>
915+
> ```rust
916+
> # fn temp() {}
917+
> # let x;
918+
> { let [_x] = [&temp()]; x = _x; } // OK
919+
> ```
920+
>
921+
> However, if we try to use `x`, even within the same statement, we'll get an error because the [temporary] is dropped at the end of this introduced block.
922+
>
901923
> ```rust,compile_fail,E0716
902924
> # fn temp() {}
903-
> # use std::convert::identity;
904-
> let x;
925+
> # let x;
905926
> ([x] = [&temp()], x); // ERROR
906927
> ```
928+
>
929+
> This desugars to:
930+
>
931+
> ```rust,compile_fail,E0716
932+
> # fn temp() {}
933+
> # let x;
934+
> (
935+
> {
936+
> let [_x] = [&temp()];
937+
> x = _x;
938+
> }, // <-- The temporary is dropped here.
939+
> x, // ERROR
940+
> );
941+
> ```
907942
908943
r[expr.compound-assign]
909944
## Compound assignment expressions
@@ -1054,6 +1089,7 @@ As with normal assignment expressions, compound assignment expressions always pr
10541089
[dropping]: ../destructors.md
10551090
[eval order test]: https://github.com/rust-lang/rust/blob/1.58.0/src/test/ui/expr/compound-assignment/eval-order.rs
10561091
[explicit discriminants]: ../items/enumerations.md#explicit-discriminants
1092+
[extending expression]: destructors.scope.lifetime-extension.exprs
10571093
[field-less enums]: ../items/enumerations.md#field-less-enum
10581094
[grouped expression]: grouped-expr.md
10591095
[literal expression]: literal-expr.md#integer-literal-expressions
@@ -1068,13 +1104,14 @@ As with normal assignment expressions, compound assignment expressions always pr
10681104
[unit]: ../types/tuple.md
10691105
[Unit-only enums]: ../items/enumerations.md#unit-only-enum
10701106
[value expression]: ../expressions.md#place-expressions-and-value-expressions
1071-
[temporary lifetime extension]: ../destructors.md#temporary-lifetime-extension
1072-
[temporary scopes]: ../destructors.md#temporary-scopes
1107+
[temporary lifetime extension]: destructors.scope.lifetime-extension
1108+
[temporary scope]: destructors.scope.temporary
10731109
[temporary value]: ../expressions.md#temporaries
10741110
[float-float]: https://github.com/rust-lang/rust/issues/15536
10751111
[Function pointer]: ../types/function-pointer.md
10761112
[Function item]: ../types/function-item.md
10771113
[receiver]: expr.method.intro
1114+
[temporary]: expr.temporary
10781115
[undefined behavior]: ../behavior-considered-undefined.md
10791116
[Underscore expressions]: ./underscore-expr.md
10801117
[range expressions]: ./range-expr.md

0 commit comments

Comments
 (0)