Skip to content

Conversation

@dianne
Copy link

@dianne dianne commented Nov 10, 2025

This adjusts the wording and examples to accommodate rust-lang/rust#146098.

### Temporary scope may be narrowed

When a temporary is created in order to evaluate an expression, the temporary is dropped based on the [temporary scope rules]. Those rules define how long the temporary will be kept alive. Before 2024, temporaries from tail expressions of a block would be extended outside the block to the next temporary scope boundary. In many cases this would be the end of a statement or function body. In 2024, the temporaries of the tail expression may now be dropped immediately at the end of the block (before any local variables in the block).
When a temporary is created in order to evaluate an expression, the temporary is dropped based on the [temporary scope rules]. Those rules define how long the temporary will be kept alive. Before 2024, temporaries from tail expressions of a block would live past the block to the next temporary scope boundary. In many cases this would be the end of a statement or function body. In 2024, the temporaries of the tail expression may now be dropped immediately at the end of the block (before any local variables in the block).
Copy link
Author

Choose a reason for hiding this comment

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

I'd like to be deliberate about using "extended" only when it applies to temporary lifetime extension. I've reworded instances where it was used to refer to a temporary scope boundary not existing at all.

// This example works in 2021, but fails to compile in 2024.
fn main() {
let x = { &String::from("1234") }.len();
let x = { String::from(" 1234 ").trim() }.len();
Copy link
Author

@dianne dianne Nov 10, 2025

Choose a reason for hiding this comment

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

The old example compiles under rust-lang/rust#146098 so it needed some reworking to make the temporary not get extended. It's still contrived, but there's some realism to taking the length of a trimmed string.

```

In this example, in 2021, the temporary `String` is extended outside of the block, past the call to `len()`, and is dropped at the end of the statement. In 2024, it is dropped immediately at the end of the block, causing a compile error about the temporary being dropped while borrowed.
In this example, in 2021, the temporary `String` lives past the call to `len()` and is dropped at the end of the statement. In 2024, it is dropped immediately at the end of the block, causing a compile error about the temporary being dropped while borrowed.
Copy link
Author

Choose a reason for hiding this comment

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

I couldn't think of a pleasant way to preserve all the details without wording it as if the absence of a temporary scope acts upon the temporary. Hopefully it still reads okay? I figure living past the .len() is the most important detail; since that happens in 2021, it implicitly lives past the block. Being dropped after the block tail is also still explicitly provided as the failure point in 2024 for contrast.

Comment on lines -71 to +76
The solution for these kinds of situations is to lift the block expression out to a local variable so that the temporary lives long enough:
One solution for these kinds of situations is to lift the temporary out to a local variable so that it lives long enough:

```rust,edition2024
fn main() {
let s = { &String::from("1234") };
let x = s.len();
let s = String::from(" 1234 ");
let x = { s.trim() }.len();
Copy link
Author

Choose a reason for hiding this comment

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

Lifting the block expression out won't make a difference to drop order within the evaluation of an expression anymore, so we needed a new suggested fix. I've gone with a pretty general one and adjusted the wording to be less prescriptive to balance it out. I wouldn't want it to sound like that's the only fix when it makes the String live for the rest of the function; sometimes that's not ideal. Suggesting something like

let x = {
    let s = String::from(" 1234 ");
    { s.trim() }.len()
};

might be better morally for that reason, but it's less pretty in the common case.

@dianne dianne marked this pull request as ready for review November 10, 2025 07:56
@rustbot rustbot added the S-waiting-on-review Status: The marked PR is awaiting some action (such as code changes) from the PR author label Nov 10, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-review Status: The marked PR is awaiting some action (such as code changes) from the PR author

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants