Skip to content

Commit dbfd0dc

Browse files
committed
Reorganize, remove hacks, clarify, and add examples
1 parent fcdc090 commit dbfd0dc

File tree

2 files changed

+61
-36
lines changed

2 files changed

+61
-36
lines changed

src/destructors.md

Lines changed: 60 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 *extended 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 extended 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 extended 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 extended scope of an extending expression is the extended 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 extended 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 extended scope.
507+
> To determine its extended scope, look outward:
508+
>
509+
> * Since borrow expressions' operands are extending, the extended scope of `temp()` is the extended 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 extended scope of its extending parent, the block expression.
511+
> * `{ &temp() }` is the initializer expression of a `let` statement, so its extended 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 extended 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,29 @@ 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 extended scope of any other expression is its [temporary scope].
528+
529+
> [!NOTE]
530+
> In this case, the expression is not extending, meaning it cannot be a borrow expression or a [super operand][super operands] to a [super macro call] expression, so its temporary scope is given by [destructors.scope.temporary.enclosing].
531+
532+
> [!EXAMPLE]
533+
> In this example, the temporary value holding the result of `temp()` is extended to the end of the statement:
534+
>
535+
> ```rust,edition2024
536+
> # fn temp() {}
537+
> # fn use_temp(_: &()) {}
538+
> use_temp({ &temp() });
539+
> ```
540+
>
541+
> `temp()` is the operand of a borrow expression, so its temporary scope is its extended scope.
542+
> To determine its extended scope, look outward:
543+
>
544+
> * Since borrow expressions' operands are extending, the extended scope of `temp()` is the extended scope of its extending parent, the borrow expression.
545+
> * `&temp()` is the final expression of a plain block. Since the final expressions of plain blocks are extending, the extended scope of `&temp()` is the extended scope of its extending parent, the block expression.
546+
> * `{ &temp() }` is the argument of a call expression, which is not extending. Since no other cases apply, its extended scope is its temporary scope.
547+
> * Per [destructors.scope.temporary.enclosing], the temporary scope of `{ &temp() }`, and thus the extended scope of `temp()`, is the scope of the statement.
548+
>
549+
> 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]).
511550
512551
#### Examples
513552
@@ -556,19 +595,6 @@ let x = format_args!("{:?}", temp()); // As above.
556595
# assert_eq!(0, X.load(Relaxed));
557596
```
558597
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-
572598
Here are some examples where expressions don't have extended temporary scopes:
573599

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

685711
[array expression]: expressions/array-expr.md#array-expressions
686712
[array repeat operands]: expr.array.repeat-operand
687-
[async block expression]: expr.block.async
688713
[block expression]: expressions/block-expr.md
689714
[borrow]: expr.operator.borrow
690715
[cast expression]: expressions/operator-expr.md#type-cast-expressions
@@ -696,11 +721,11 @@ There is one additional case to be aware of: when a panic reaches a [non-unwindi
696721
[struct expression]: expressions/struct-expr.md
697722
[super macro call]: expr.super-macros
698723
[super operands]: expr.super-macros
699-
[super temporaries]: expr.super-macros
724+
[super temporary]: expr.super-macros
700725
[temporary scope]: destructors.scope.temporary
701-
[temporary scopes]: destructors.scope.temporary
702726
[tuple expression]: expressions/tuple-expr.md#tuple-expressions
703727
[tuple indexing expression]: expressions/tuple-expr.md#tuple-indexing-expressions
728+
[`unsafe` block expression]: expr.block.unsafe
704729

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

src/items/constant-items.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ const C: &mut u8 = unsafe { &mut S }; // ERROR not allowed
7777
> const C: &AtomicU8 = &AtomicU8::new(0); // ERROR
7878
> ```
7979
>
80-
> Here, the `AtomicU8` is a temporary that is lifetime extended to `'static` (see [destructors.scope.lifetime-extension.static]), and references to lifetime-extended temporaries with interior mutability are not allowed in the final value of a constant expression (see [const-eval.const-expr.borrows]).
80+
> Here, the `AtomicU8` is a temporary that is lifetime extended to `'static` (see [destructors.scope.lifetime-extension.exprs.static]), and references to lifetime-extended temporaries with interior mutability are not allowed in the final value of a constant expression (see [const-eval.const-expr.borrows]).
8181
8282
r[items.const.expr-omission]
8383
The constant expression may only be omitted in a [trait definition].

0 commit comments

Comments
 (0)