Skip to content

Commit e856ddf

Browse files
committed
Specify lifetime extension through expressions
1 parent e122eef commit e856ddf

File tree

1 file changed

+44
-33
lines changed

1 file changed

+44
-33
lines changed

src/destructors.md

Lines changed: 44 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -474,36 +474,47 @@ r[destructors.scope.lifetime-extension.exprs]
474474
#### Extending based on expressions
475475

476476
r[destructors.scope.lifetime-extension.exprs.extending]
477-
For a let statement with an initializer, an *extending expression* is an
478-
expression which is one of the following:
477+
An *extending expression* is an expression which is one of the following:
479478

480-
* The initializer expression.
481-
* The operand of an extending [borrow] expression.
482-
* The [super operands] of an extending [super macro call] expression.
483-
* The operand(s) of an extending [array][array expression], [cast][cast
479+
* The initializer expression of a `let` statement or the body expression of a [static][static item] or [constant item].
480+
* The operand of a [borrow] expression.
481+
* The [super operands] of a [super macro call] expression.
482+
* The operand(s) of an [array][array expression], [cast][cast
484483
expression], [braced struct][struct expression], or [tuple][tuple expression]
485484
expression.
486-
* The arguments to an extending [tuple struct] or [tuple enum variant] constructor expression.
487-
* The final expression of an extending [block expression] except for an [async block expression].
488-
* The final expression of an extending [`if`] expression's consequent, `else if`, or `else` block.
489-
* An arm expression of an extending [`match`] expression.
485+
* The arguments to a [tuple struct] or [tuple enum variant] constructor expression.
486+
* The final expression of a [block expression] except for an [async block expression].
487+
* The final expression of an [`if`] expression's consequent, `else if`, or `else` block.
488+
* An arm expression of a [`match`] expression.
490489

491490
> [!NOTE]
492491
> 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].
493492
494-
So the borrow expressions in `&mut 0`, `(&1, &mut 2)`, and `Some(&mut 3)`
493+
> [!NOTE]
494+
> `rustc` does not treat [array repeat operands] of [array] expressions as extending expressions. Whether it should is an open question.
495+
>
496+
> For details, see [Rust issue #146092](https://github.com/rust-lang/rust/issues/146092).
497+
498+
So the borrow expressions in `{ &mut 0 }`, `(&1, &mut 2)`, and `Some(&mut 3)`
495499
are all extending expressions. The borrows in `&0 + &1` and `f(&mut 0)` are not.
496500

497501
r[destructors.scope.lifetime-extension.exprs.borrows]
498-
The operand of an extending [borrow] expression has its [temporary scope] [extended].
502+
The [temporary scope] of the operand of a [borrow] expression is *extended through* the scope of the borrow expression.
499503

500504
r[destructors.scope.lifetime-extension.exprs.super-macros]
501-
The [super temporaries] of an extending [super macro call] expression have their [scopes][temporary scopes] [extended].
505+
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.
502506

503-
> [!NOTE]
504-
> `rustc` does not treat [array repeat operands] of extending [array] expressions as extending expressions. Whether it should is an open question.
505-
>
506-
> For details, see [Rust issue #146092](https://github.com/rust-lang/rust/issues/146092).
507+
r[destructors.scope.lifetime-extension.exprs.parent]
508+
If a temporary scope is extended through the scope of an extending expression, it is extended through that scope's [parent][destructors.scope.nesting].
509+
510+
r[destructors.scope.lifetime-extension.exprs.let]
511+
A temporary scope extended through a `let` statement scope is [extended] to the scope of the block containing the `let` statement ([destructors.scope.lifetime-extension.let]).
512+
513+
r[destructors.scope.lifetime-extension.exprs.static]
514+
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 ([destructors.scope.lifetime-extension.static]).
515+
516+
r[destructors.scope.lifetime-extension.exprs.other]
517+
A temporary scope extended through the scope of a non-extending expression is [extended] to that expression's [temporary scope].
507518

508519
#### Examples
509520

@@ -552,6 +563,19 @@ let x = format_args!("{:?}", temp()); // As above.
552563
# assert_eq!(0, X.load(Relaxed));
553564
```
554565

566+
```rust,edition2024
567+
# fn temp() {}
568+
# fn use_temp(_: &()) {}
569+
// The final expression of a block is extending. Since the block below
570+
// is not itself extending, the temporary is extended to the block
571+
// expression's temporary scope, ending at the semicolon.
572+
use_temp({ &temp() });
573+
// As above, the final expressions of `if`/`else` blocks are
574+
// extending, which extends the temporaries to the `if` expression's
575+
// temporary scope.
576+
use_temp(if true { &temp() } else { &temp() });
577+
```
578+
555579
Here are some examples where expressions don't have extended temporary scopes:
556580

557581
```rust,compile_fail,E0716
@@ -606,22 +630,6 @@ let x = 'a: { break 'a &temp() }; // ERROR
606630
# x;
607631
```
608632

609-
```rust,edition2024,compile_fail,E0716
610-
# use core::pin::pin;
611-
# fn temp() {}
612-
// The argument to `pin!` is only an extending expression if the call
613-
// is an extending expression. Since it's not, the inner block is not
614-
// an extending expression, so the temporaries in its trailing
615-
// expression are dropped immediately.
616-
pin!({ &temp() }); // ERROR
617-
```
618-
619-
```rust,edition2024,compile_fail,E0716
620-
# fn temp() {}
621-
// As above.
622-
format_args!("{:?}", { &temp() }); // ERROR
623-
```
624-
625633
r[destructors.forget]
626634
## Not running destructors
627635

@@ -647,6 +655,7 @@ There is one additional case to be aware of: when a panic reaches a [non-unwindi
647655
[Assignment]: expressions/operator-expr.md#assignment-expressions
648656
[binding modes]: patterns.md#binding-modes
649657
[closure]: types/closure.md
658+
[constant item]: items/constant-items.md
650659
[destructors]: destructors.md
651660
[destructuring assignment]: expr.assign.destructure
652661
[expression]: expressions.md
@@ -660,6 +669,7 @@ There is one additional case to be aware of: when a panic reaches a [non-unwindi
660669
[promoted]: destructors.md#constant-promotion
661670
[scrutinee]: glossary.md#scrutinee
662671
[statement]: statements.md
672+
[static item]: items/static-items.md
663673
[temporary]: expressions.md#temporaries
664674
[unwinding]: panic.md#unwinding
665675
[variable]: variables.md
@@ -685,6 +695,7 @@ There is one additional case to be aware of: when a panic reaches a [non-unwindi
685695
[block expression]: expressions/block-expr.md
686696
[borrow]: expr.operator.borrow
687697
[cast expression]: expressions/operator-expr.md#type-cast-expressions
698+
[const block expression]: expr.block.const
688699
[dereference expression]: expressions/operator-expr.md#the-dereference-operator
689700
[extended]: destructors.scope.lifetime-extension
690701
[field expression]: expressions/field-expr.md

0 commit comments

Comments
 (0)