Skip to content

Commit 51c2e99

Browse files
committed
Reorganize, remove hacks, clarify, and add examples
1 parent 593f1ac commit 51c2e99

File tree

1 file changed

+57
-35
lines changed

1 file changed

+57
-35
lines changed

src/destructors.md

Lines changed: 57 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -458,19 +458,24 @@ let &ref x = &*&temp(); // OK
458458
r[destructors.scope.lifetime-extension.exprs]
459459
#### Extending based on expressions
460460

461+
r[destructors.scope.lifetime-extension.exprs.borrows]
462+
The [temporary scope] of the operand of a [borrow] expression is the *borrow scope* of the operand expression, defined below.
463+
464+
r[destructors.scope.lifetime-extension.exprs.super-macros]
465+
The [scope][temporary scope] of each [super temporary] of a [super macro call] expression is the borrow scope of the super macro call expression.
466+
461467
r[destructors.scope.lifetime-extension.exprs.extending]
462-
An *extending expression* is an expression which is one of the following:
468+
The borrow scope of an expression is defined in terms of *extending expressions* and their *extending parents*. An extending expression is an expression which is one of the following:
463469

464-
* The initializer expression of a `let` statement or the body expression of a [static][static item] or [constant item].
465-
* The operand of a [borrow] expression.
466-
* The [super operands] of a [super macro call] expression.
470+
* The operand of a [borrow] expression, the extending parent of which is the borrow expression.
471+
* The [super operands] of a [super macro call] expression, the extending parent of which is the macro call expression.
467472
* The operand(s) of an [array][array expression], [cast][cast
468473
expression], [braced struct][struct expression], or [tuple][tuple expression]
469-
expression.
470-
* The arguments to a [tuple struct] or [tuple enum variant] constructor expression.
471-
* The final expression of a [block expression] except for an [async block expression].
472-
* The final expression of an [`if`] expression's consequent, `else if`, or `else` block.
473-
* An arm expression of a [`match`] expression.
474+
expression, the extending parent of which is the array, cast, braced struct, or tuple expression.
475+
* The arguments to a [tuple struct] or [tuple enum variant] constructor expression, the extending parent of which is the constructor expression.
476+
* The final expression of a plain [block expression] or [`unsafe` block expression], the extending parent of which is the block expression.
477+
* The final expression of an [`if`] expression's consequent, `else if`, or `else` block, the extending parent of which is the `if` expression.
478+
* An arm expression of a [`match`] expression, the extending parent of which is the `match` expression.
474479

475480
> [!NOTE]
476481
> 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].
@@ -483,20 +488,32 @@ An *extending expression* is an expression which is one of the following:
483488
So the borrow expressions in `{ &mut 0 }`, `(&1, &mut 2)`, and `Some(&mut 3)`
484489
are all extending expressions. The borrows in `&0 + &1` and `f(&mut 0)` are not.
485490

486-
r[destructors.scope.lifetime-extension.exprs.borrows]
487-
The [temporary scope] of the operand of a [borrow] expression is *extended through* the scope of the borrow expression.
488-
489-
r[destructors.scope.lifetime-extension.exprs.super-macros]
490-
The [scopes][temporary scopes] of the [super temporaries] of an extending [super macro call] expression are *extended through* the scope of the super macro call expression.
491-
492491
r[destructors.scope.lifetime-extension.exprs.parent]
493-
If a temporary scope is extended through the scope of an extending expression, it is extended through that scope's [parent][destructors.scope.nesting].
492+
The borrow scope of an extending expression is the borrow scope of its extending parent.
494493

495494
r[destructors.scope.lifetime-extension.exprs.let]
496-
A temporary scope extended through a `let` statement scope is [extended] to the scope of the block containing the `let` statement.
495+
The borrow scope of the initializer expression of a `let` statement is the scope of the block containing the `let` statement.
496+
497+
> [!EXAMPLE]
498+
> In this example, the temporary value holding the result of `temp()` is extended to the end of the block in which `x` is declared:
499+
>
500+
> ```rust,edition2024
501+
> # fn temp() {}
502+
> let x = { &temp() };
503+
> println!("{x:?}");
504+
> ```
505+
>
506+
> `temp()` is the operand of a borrow expression, so its temporary scope is its borrow scope.
507+
> To determine its borrow scope, look outward:
508+
>
509+
> * Since borrow expressions' operands are extending, the borrow scope of `temp()` is the borrow scope of its extending parent, the borrow expression.
510+
> * `&temp()` is the final expression of a plain block. Since the final expressions of plain blocks are extending, the extended temporary scope of `&temp()` is the borrow scope of its extending parent, the block expression.
511+
> * `{ &temp() }` is the initializer expression of a `let` statement, so its borrow scope is the scope of the block containg that `let` statement.
512+
>
513+
> If not for temporary lifetime extension, the result of `temp()` would be dropped after evaluating the tail expression of the block `{ &temp() }` ([destructors.scope.temporary.enclosing]).
497514
498515
r[destructors.scope.lifetime-extension.exprs.static]
499-
A temporary scope extended through a [static][static item] or [constant item] scope or a [const block][const block expression] scope is [extended] to the end of the program.
516+
The borrow scope of the body expression of a [static][static item] or [constant item], and of the final expression of a [const block expression], is the entire program. This prevents destructors from being run.
500517
501518
```rust
502519
const C: &Vec<i32> = &Vec::new();
@@ -507,7 +524,26 @@ println!("{:?}", C);
507524
```
508525
509526
r[destructors.scope.lifetime-extension.exprs.other]
510-
A temporary scope extended through the scope of a non-extending expression is [extended] to that expression's [temporary scope].
527+
The borrow scope of any other expression is its non-extended temporary scope, as defined by [destructors.scope.temporary.enclosing].
528+
529+
> [!EXAMPLE]
530+
> In this example, the temporary value holding the result of `temp()` is extended to the end of the statement:
531+
>
532+
> ```rust,edition2024
533+
> # fn temp() {}
534+
> # fn use_temp(_: &()) {}
535+
> use_temp({ &temp() });
536+
> ```
537+
>
538+
> `temp()` is the operand of a borrow expression, so its temporary scope is its borrow scope.
539+
> To determine its borrow scope, look outward:
540+
>
541+
> * Since borrow expressions' operands are extending, the borrow scope of `temp()` is the borrow scope of its extending parent, the borrow expression.
542+
> * `&temp()` is the final expression of a plain block. Since the final expressions of plain blocks are extending, the borrow scope of `&temp()` is the borrow scope of its extending parent, the block expression.
543+
> * `{ &temp() }` is the argument of a call expression, which is not extending. Since no other cases apply, its borrow scope is its temporary scope.
544+
> * Per [destructors.scope.temporary.enclosing], the temporary scope of `{ &temp() }`, and thus the borrow scope of `temp()`, is the scope of the statement.
545+
>
546+
> If not for temporary lifetime extension, the result of `temp()` would be dropped after evaluating the tail expression of the block `{ &temp() }` ([destructors.scope.temporary.enclosing]).
511547
512548
#### Examples
513549
@@ -556,19 +592,6 @@ let x = format_args!("{:?}", temp()); // As above.
556592
# assert_eq!(0, X.load(Relaxed));
557593
```
558594
559-
```rust,edition2024
560-
# fn temp() {}
561-
# fn use_temp(_: &()) {}
562-
// The final expression of a block is extending. Since the block below
563-
// is not itself extending, the temporary is extended to the block
564-
// expression's temporary scope, ending at the semicolon.
565-
use_temp({ &temp() });
566-
// As above, the final expressions of `if`/`else` blocks are
567-
// extending, which extends the temporaries to the `if` expression's
568-
// temporary scope.
569-
use_temp(if true { &temp() } else { &temp() });
570-
```
571-
572595
Here are some examples where expressions don't have extended temporary scopes:
573596

574597
```rust,compile_fail,E0716
@@ -684,7 +707,6 @@ There is one additional case to be aware of: when a panic reaches a [non-unwindi
684707

685708
[array expression]: expressions/array-expr.md#array-expressions
686709
[array repeat operands]: expr.array.repeat-operand
687-
[async block expression]: expr.block.async
688710
[block expression]: expressions/block-expr.md
689711
[borrow]: expr.operator.borrow
690712
[cast expression]: expressions/operator-expr.md#type-cast-expressions
@@ -696,11 +718,11 @@ There is one additional case to be aware of: when a panic reaches a [non-unwindi
696718
[struct expression]: expressions/struct-expr.md
697719
[super macro call]: expr.super-macros
698720
[super operands]: expr.super-macros
699-
[super temporaries]: expr.super-macros
721+
[super temporary]: expr.super-macros
700722
[temporary scope]: destructors.scope.temporary
701-
[temporary scopes]: destructors.scope.temporary
702723
[tuple expression]: expressions/tuple-expr.md#tuple-expressions
703724
[tuple indexing expression]: expressions/tuple-expr.md#tuple-indexing-expressions
725+
[`unsafe` block expression]: expr.block.unsafe
704726

705727
[`for`]: expressions/loop-expr.md#iterator-loops
706728
[`if let`]: expressions/if-expr.md#if-let-patterns

0 commit comments

Comments
 (0)