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
22 changes: 20 additions & 2 deletions src/destructors.md
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,7 @@ scope of the initializer expression is extended.
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
expression which is one of the following:

Expand All @@ -434,29 +435,41 @@ expression which is one of the following:
expression], [braced struct][struct expression], or [tuple][tuple expression]
expression.
* The arguments to an extending [tuple struct] or [tuple variant] constructor expression.
* The argument(s) to an extending [`pin!`] or [`format_args!`] [macro invocation] expression.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ideally this should be clearer about the format arguments being extended, not the format string (which doesn't need extension), but I've had trouble including that detail without breaking the flow/clarity of the section

* The final expression of any extending [block expression].

So the borrow expressions in `&mut 0`, `(&1, &mut 2)`, and `Some(&mut 3)`
are all extending expressions. The borrows in `&0 + &1` and `f(&mut 0)` are not.

r[destructors.scope.lifetime-extension.exprs.borrow]
The operand of any extending borrow expression has its temporary scope
extended.

r[destructors.scope.lifetime-extension.exprs.macros]
The built-in macros [`pin!`] and [`format_args!`] create temporaries.
Any extending [`pin!`] or [`format_args!`] [macro invocation] expression has an extended temporary scope.
Comment on lines +444 to +450
Copy link
Contributor Author

@dianne dianne Aug 28, 2025

Choose a reason for hiding this comment

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

Fourth pass: I've broken up destructors.scope.lifetime-extension.exprs to match destructors.scope.lifetime-extension.patterns and to put the rule for built-in macros' temporaries in a subsection. I've also moved the rule for arguments' extension back into the the newly-delimited rule destructors.scope.lifetime-extension.exprs.extending. I'm not satisfied with the wording yet, but structurally I think it's an improvement.

I'm doing a bit of conflation here. The "temporaries" here are both:

  • super let bindings; since they have (extended) temporary scopes, I feel like referring to them as "temporaries" is most fitting for the moment.
  • The borrowed temporaries created when a value expression is passed to format_args!.

Let me know if it needs further clarification. My hope is that it's a suitable level of detail for how these macros behave, to avoid specifying their exact expansion.


#### Examples

Here are some examples where expressions have extended temporary scopes:

```rust
# use std::pin::pin;
# fn temp() {}
# trait Use { fn use_temp(&self) -> &Self { self } }
# impl Use for () {}
// The temporary that stores the result of `temp()` lives in the same scope
// as x in these cases.
let x = &temp();
# x;
let x = &temp() as &dyn Send;
# x;
let x = (&*&temp(),);
# x;
let x = pin!(temp());
# x;
let x = { [Some(&temp()) ] };
# x;
let ref x = temp();
# x;
let ref x = *&temp();
# x;
```
Expand All @@ -471,6 +484,7 @@ Here are some examples where expressions don't have extended temporary scopes:
// end of the let statement in these cases.

let x = std::convert::identity(&temp()); // ERROR
# x;
let x = (&temp()).use_temp(); // ERROR
# x;
```
Expand Down Expand Up @@ -506,6 +520,7 @@ There is one additional case to be aware of: when a panic reaches a [non-unwindi
[initialized]: glossary.md#initialized
[interior mutability]: interior-mutability.md
[lazy boolean expression]: expressions/operator-expr.md#lazy-boolean-operators
[macro invocation]: macros.md#macro-invocation
[non-unwinding ABI boundary]: items/functions.md#unwinding
[panic]: panic.md
[place context]: expressions.md#place-expressions-and-value-expressions
Expand Down Expand Up @@ -550,3 +565,6 @@ There is one additional case to be aware of: when a panic reaches a [non-unwindi
[`match`]: expressions/match-expr.md
[`while let`]: expressions/loop-expr.md#while-let-patterns
[`while`]: expressions/loop-expr.md#predicate-loops

[`pin!`]: std::pin::pin
[`format_args!`]: core::format_args