Skip to content

Commit fb440e8

Browse files
committed
Reorganize, remove hacks, clarify, and add examples
1 parent 596d282 commit fb440e8

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
@@ -175,7 +175,7 @@ const _: &&mut u8 = unsafe { &S }; // OK.
175175
> const _: &AtomicU8 = &AtomicU8::new(0); // ERROR.
176176
> ```
177177
>
178-
> Here, the `AtomicU8` is a temporary whose scope is extended to the end of the program (see [destructors.scope.lifetime-extension.static]). Such temporaries with interior mutability cannot be borrowed in constant expressions (see [const-eval.const-expr.borrows]).
178+
> Here, the `AtomicU8` is a temporary whose scope is extended to the end of the program (see [destructors.scope.lifetime-extension.exprs.static]). Such temporaries with interior mutability cannot be borrowed in constant expressions (see [const-eval.const-expr.borrows]).
179179
>
180180
> To allow this, we'd have to decide whether each use of the constant creates a new `AtomicU8` or whether each use shares the same lifetime-extended temporary. The latter choice, though closer to how `rustc` thinks about this today, would break the conceptual model that, in most cases, the constant initializer can be thought of as being inlined wherever the constant is used. Since we haven't decided, this is not allowed.
181181

0 commit comments

Comments
 (0)