Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 24 additions & 17 deletions src/destructors.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,10 @@ Overloaded operators are not distinguished from built-in operators and [binding
modes] are not considered.

r[destructors.scope.list]
Given a function, or closure, there are drop scopes for:
Given a function, closure, [static][static item] or [constant item], or [const block][const block expression], there are drop scopes for:

r[destructors.scope.function]
* The entire function
r[destructors.scope.body]
* The entire function, the body of a static or constant item, or a const block

r[destructors.scope.statement]
* Each [statement]
Expand All @@ -101,11 +101,11 @@ Drop scopes are nested within one another as follows. When multiple scopes are
left at once, such as when returning from a function, variables are dropped
from the inside outwards.

r[destructors.scope.nesting.function]
* The entire function scope is the outer most scope.
r[destructors.scope.nesting.body]
* The entire function, static or constant item, or const block scope is the outer most scope.

r[destructors.scope.nesting.function-body]
* The function body block is contained within the scope of the entire function.
* For functions, the function body block is contained within the scope of the entire function.

r[destructors.scope.nesting.expr-statement]
* The parent of the expression in an expression statement is the scope of the
Expand Down Expand Up @@ -251,7 +251,7 @@ r[destructors.scope.temporary.enclosing]
Apart from lifetime extension, the temporary scope of an expression is the
smallest scope that contains the expression and is one of the following:

* The entire function.
* The entire body of the function, [static item], [constant item], or [const block][const block expression].
* A statement.
* The body of an [`if`], [`while`] or [`loop`] expression.
* The `else` block of an `if` expression.
Expand Down Expand Up @@ -389,15 +389,21 @@ println!("{}", x);
```

r[destructors.scope.lifetime-extension.static]
Lifetime extension also applies to `static` and `const` items, where it
makes temporaries live until the end of the program. For example:
Lifetime extension also applies to [static][static item] and [constant items][constant item] and to [const blocks][const block expression], where it
makes temporaries live until the end of the program. This prevents their destructors from being run. For example:

```rust
const C: &Vec<i32> = &Vec::new();
// Usually this would be a dangling reference as the `Vec` would only
// exist inside the initializer expression of `C`, but instead the
# #[derive(Debug)] struct PanicOnDrop;
# impl Drop for PanicOnDrop { fn drop(&mut self) { panic!() } }
# const fn temp() -> PanicOnDrop { PanicOnDrop }
const C: &PanicOnDrop = &temp();
// Usually this would be a dangling reference as the result of `temp()` would
// only exist inside the initializer expression of `C`, but instead the
// borrow gets lifetime-extended so it effectively has `'static` lifetime.
println!("{:?}", C);
// `const` blocks may likewise extend temporaries to the end of the program:
// the result of `temp()` is not dropped.
println!("{:?}", const { &temp() });
```

r[destructors.scope.lifetime-extension.sub-expressions]
Expand Down Expand Up @@ -474,17 +480,17 @@ r[destructors.scope.lifetime-extension.exprs]
#### Extending based on expressions

r[destructors.scope.lifetime-extension.exprs.extending]
For a let statement with an initializer, an *extending expression* is an
For a let statement with an initializer, a [static item], a [constant item], or a [const block][const block expression], an *extending expression* is an
expression which is one of the following:

* The initializer expression.
* The initializer expression of a let statement, the body of a static or constant item, or the final expression of a const block.
* The operand of an extending [borrow] expression.
* The [super operands] of an extending [super macro call] expression.
* The operand(s) of an extending [array][array expression], [cast][cast
expression], [braced struct][struct expression], or [tuple][tuple expression]
expression.
* The arguments to an extending [tuple struct] or [tuple enum variant] constructor expression.
* The final expression of an extending [block expression] except for an [async block expression].
* The final expression of an extending [block expression] except for an [async block expression] or const block expression.
Comment on lines -477 to +493
Copy link
Contributor Author

@dianne dianne Oct 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This in particular feels awkward to me, but I'm not sure how to make it read better without entirely reworking the format of the list and/or making "extending" a binary relation (mapping expressions to extended scopes). The first bullet point is a base case, which effectively duplicates destructors.scope.lifetime-extension.let (and now destructors.scope.lifetime-extension.static), except without specifying the scope of extended temporaries. This gets a bit messy for const blocks in particular: const blocks are one of the base cases (they extend temporaries to statics) but they also have to be an exception to the grammar for extending expressions (so that we're not also specifying that they extend temporaries to the end of a block when they appear in a let initializer).

* The final expression of an extending [`if`] expression's consequent, `else if`, or `else` block.
* An arm expression of an extending [`match`] expression.

Expand Down Expand Up @@ -531,8 +537,6 @@ let x = Some(&temp()); // Argument to tuple enum variant constructor.
# x;
let x = { [Some(&temp())] }; // Final expr of block.
# x;
let x = const { &temp() }; // Final expr of `const` block.
# x;
let x = unsafe { &temp() }; // Final expr of `unsafe` block.
# x;
let x = if true { &temp() } else { &temp() };
Expand Down Expand Up @@ -647,6 +651,7 @@ There is one additional case to be aware of: when a panic reaches a [non-unwindi
[Assignment]: expressions/operator-expr.md#assignment-expressions
[binding modes]: patterns.md#binding-modes
[closure]: types/closure.md
[constant item]: items/constant-items.md
[destructors]: destructors.md
[destructuring assignment]: expr.assign.destructure
[expression]: expressions.md
Expand All @@ -660,6 +665,7 @@ There is one additional case to be aware of: when a panic reaches a [non-unwindi
[promoted]: destructors.md#constant-promotion
[scrutinee]: glossary.md#scrutinee
[statement]: statements.md
[static item]: items/static-items.md
[temporary]: expressions.md#temporaries
[unwinding]: panic.md#unwinding
[variable]: variables.md
Expand All @@ -683,6 +689,7 @@ There is one additional case to be aware of: when a panic reaches a [non-unwindi
[array repeat operands]: expr.array.repeat-operand
[async block expression]: expr.block.async
[block expression]: expressions/block-expr.md
[const block expression]: expr.block.const
[borrow]: expr.operator.borrow
[cast expression]: expressions/operator-expr.md#type-cast-expressions
[dereference expression]: expressions/operator-expr.md#the-dereference-operator
Expand Down