Skip to content

Temporary lifetime extension through tuple struct and tuple variant constructors #140593

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jun 14, 2025

Conversation

m-ou-se
Copy link
Member

@m-ou-se m-ou-se commented May 2, 2025

This makes temporary lifetime extension work for tuple struct and tuple variant constructors, such as Some().

Before:

let a = &temp(); // Extended
let a = Some(&temp()); // Not extended :(
let a = Some { 0: &temp() }; // Extended

After:

let a = &temp(); // Extended
let a = Some(&temp()); // Extended
let a = Some { 0: &temp() }; // Extended

So, with this change, this works:

let a = Some(&String::from("hello")); // New: String lifetime now extended!

println!("{a:?}");

Until now, we did not extend through tuple struct/variant constructors (like Some), because they are function calls syntactically, and we do not want to extend the String lifetime in:

let a = some_function(&String::from("hello")); // String not extended!

However, it turns out to be very easy to distinguish between regular functions and constructors at the point where we do lifetime extension.

In practice, constructors nearly always use UpperCamelCase while regular functions use lower_snake_case, so it should still be easy to for a human programmer at the call site to see whether something qualifies for lifetime extension or not.

This needs a lang fcp.


More examples of what will work after this change:

let x = Person {
    name: "Ferris",
    job: Some(&Job { // `Job` now extended!
        title: "Chief Rustacean",
        organisation: "Acme Ltd.",
    }),
};

dbg!(x);
let file = if use_stdout {
    None
} else {
    Some(&File::create("asdf")?) // `File` now extended!
};

set_logger(file);
use std::path::Component;

let c = Component::Normal(&OsString::from(format!("test-{num}"))); // OsString now extended!

assert_eq!(path.components.first().unwrap(), c);

@m-ou-se m-ou-se added T-lang Relevant to the language team needs-fcp This change is insta-stable, or significant enough to need a team FCP to proceed. I-lang-nominated Nominated for discussion during a lang team meeting. labels May 2, 2025
@rustbot
Copy link
Collaborator

rustbot commented May 2, 2025

r? @Nadrieril

rustbot has assigned @Nadrieril.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label May 2, 2025
@rustbot rustbot added A-tidy Area: The tidy tool T-bootstrap Relevant to the bootstrap subteam: Rust's build system (x.py and src/bootstrap) labels May 2, 2025
@m-ou-se m-ou-se removed T-bootstrap Relevant to the bootstrap subteam: Rust's build system (x.py and src/bootstrap) A-tidy Area: The tidy tool labels May 2, 2025
@rustbot rustbot added A-tidy Area: The tidy tool T-bootstrap Relevant to the bootstrap subteam: Rust's build system (x.py and src/bootstrap) labels May 2, 2025
@m-ou-se m-ou-se removed T-bootstrap Relevant to the bootstrap subteam: Rust's build system (x.py and src/bootstrap) A-tidy Area: The tidy tool labels May 2, 2025
@m-ou-se
Copy link
Member Author

m-ou-se commented May 2, 2025

Does this count as 'I-lang-easy-decision'? I think it might be an easy decision.

@m-ou-se m-ou-se added the I-lang-easy-decision Issue: The decision needed by the team is conjectured to be easy; this does not imply nomination label May 2, 2025
Copy link
Contributor

@lcnr lcnr left a comment

Choose a reason for hiding this comment

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

please add an explicit test for

let indir = Some;
let x = indir(&temp);

@m-ou-se m-ou-se force-pushed the some-temp branch 2 times, most recently from 53924ca to a9544f2 Compare May 3, 2025 07:02
@rustbot rustbot added A-tidy Area: The tidy tool T-bootstrap Relevant to the bootstrap subteam: Rust's build system (x.py and src/bootstrap) labels May 3, 2025
@m-ou-se m-ou-se added S-waiting-on-team Status: Awaiting decision from the relevant subteam (see the T-<team> label). and removed T-bootstrap Relevant to the bootstrap subteam: Rust's build system (x.py and src/bootstrap) S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. A-tidy Area: The tidy tool labels May 3, 2025
@joshtriplett
Copy link
Member

This seems great to me. As you said, we already do this for struct constructors in general, except tuple struct constructors. This is a consistency improvement and a usability improvement.

@rfcbot merge

@rfcbot
Copy link
Collaborator

rfcbot commented May 3, 2025

Team member @joshtriplett has proposed to merge this. The next step is review by the rest of the tagged team members:

No concerns currently listed.

Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

cc @rust-lang/lang-advisors: FCP proposed for lang, please feel free to register concerns.
See this document for info about what commands tagged team members can give me.

@traviscross traviscross removed the I-lang-easy-decision Issue: The decision needed by the team is conjectured to be easy; this does not imply nomination label May 25, 2025
@apiraino apiraino removed the to-announce Announce this issue on triage meeting label Jun 5, 2025
@bors
Copy link
Collaborator

bors commented Jun 13, 2025

☔ The latest upstream changes (presumably #142438) made this pull request unmergeable. Please resolve the merge conflicts.

@bors bors added the S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. label Jun 13, 2025
@m-ou-se
Copy link
Member Author

m-ou-se commented Jun 13, 2025

rust-lang/reference#1813 is ready. (Just waiting for this PR to be merged, so the code test passes.)

@m-ou-se m-ou-se added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-bootstrap Relevant to the bootstrap subteam: Rust's build system (x.py and src/bootstrap) A-tidy Area: The tidy tool labels Jun 13, 2025
ehuss pushed a commit to m-ou-se/reference that referenced this pull request Jun 13, 2025
@ehuss ehuss removed the S-waiting-on-documentation Status: Waiting on approved PRs to documentation before merging label Jun 13, 2025
@ehuss
Copy link
Contributor

ehuss commented Jun 13, 2025

I have removed the S-waiting-on-documentation label. I think this should be ready to merge per #140593 (comment).

@m-ou-se
Copy link
Member Author

m-ou-se commented Jun 13, 2025

@bors r=Nadrieril

@bors
Copy link
Collaborator

bors commented Jun 13, 2025

📌 Commit 855ea48 has been approved by Nadrieril

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jun 13, 2025
@traviscross
Copy link
Contributor

traviscross commented Jun 14, 2025

As just a note for our possible later analysis, it's interesting to analyze, actually, why this is (conjecturally) not a new SemVer hazard.

I'd mostly expect for this,

pub struct W<T>(pub T);

and this,

mod outer {
    #[allow(dead_code, hidden_glob_reexports, non_snake_case)]
    fn W() {} //~ For namespace masking.
    pub use self::inner::*;
    mod inner {
        pub struct W<T>(pub T);
    }
}
pub use outer::W;
#[allow(non_snake_case)]
pub fn W<T>(x: T) -> W<T> {
    W { 0: x }
}

to be equivalent and indistinguishable today. If they were, then this PR would mean that you newly couldn't switch from the first to the second, e.g. to do additional work in the constructor, without a breaking change.

As it is, though, it'd already be breaking, as we disallow using the tuple form in patterns when the constructor (in the value namespace) isn't in scope. So with the first form, let W(_) = W { 0: () } works, while it doesn't for the second.

Probably, actually, I find that a bit surprising. In pattern position, I think of the tuple struct syntax W(_) as simple sugar for the W { 0: _ } form, and so probably I wouldn't expect what's in scope in the value namespace to matter. This model of tuple struct syntax in pattern position came up as well in:

jhpratt added a commit to jhpratt/rust that referenced this pull request Jun 14, 2025
Temporary lifetime extension through tuple struct and tuple variant constructors

This makes temporary lifetime extension work for tuple struct and tuple variant constructors, such as `Some()`.

Before:
```rust
let a = &temp(); // Extended
let a = Some(&temp()); // Not extended :(
let a = Some { 0: &temp() }; // Extended
```

After:
```rust
let a = &temp(); // Extended
let a = Some(&temp()); // Extended
let a = Some { 0: &temp() }; // Extended
```

So, with this change, this works:

```rust
let a = Some(&String::from("hello")); // New: String lifetime now extended!

println!("{a:?}");
```

Until now, we did not extend through tuple struct/variant constructors (like `Some`), because they are function calls syntactically, and we do not want to extend the String lifetime in:

```rust
let a = some_function(&String::from("hello")); // String not extended!
```

However, it turns out to be very easy to distinguish between regular functions and constructors at the point where we do lifetime extension.

In practice, constructors nearly always use UpperCamelCase while regular functions use lower_snake_case, so it should still be easy to for a human programmer at the call site to see whether something qualifies for lifetime extension or not.

This needs a lang fcp.

---

More examples of what will work after this change:

```rust
let x = Person {
    name: "Ferris",
    job: Some(&Job { // `Job` now extended!
        title: "Chief Rustacean",
        organisation: "Acme Ltd.",
    }),
};

dbg!(x);
```

```rust
let file = if use_stdout {
    None
} else {
    Some(&File::create("asdf")?) // `File` now extended!
};

set_logger(file);
```

```rust
use std::path::Component;

let c = Component::Normal(&OsString::from(format!("test-{num}"))); // OsString now extended!

assert_eq!(path.components.first().unwrap(), c);
```
bors added a commit that referenced this pull request Jun 14, 2025
Rollup of 9 pull requests

Successful merges:

 - #140593 (Temporary lifetime extension through tuple struct and tuple variant constructors)
 - #141399 ([rustdoc] Give more information into extracted doctest information)
 - #141493 (Delegate `<SocketAddr as Debug>` to `ByteStr`)
 - #141811 (Unimplement unsized_locals)
 - #142243 (float tests: deduplicate min, max, and rounding tests)
 - #142464 (variadic functions: remove list of supported ABIs from error)
 - #142477 (Fix incorrect suggestion when calling an associated type with a type anchor)
 - #142484 (Remove unneeded lifetime bound from signature of BTreeSet::extract_if)
 - #142489 (Update the `compiler-builtins` subtree)

r? `@ghost`
`@rustbot` modify labels: rollup
@bors bors merged commit ade745e into rust-lang:master Jun 14, 2025
10 checks passed
@rustbot rustbot added this to the 1.89.0 milestone Jun 14, 2025
rust-timer added a commit that referenced this pull request Jun 14, 2025
Rollup merge of #140593 - m-ou-se:some-temp, r=Nadrieril

Temporary lifetime extension through tuple struct and tuple variant constructors

This makes temporary lifetime extension work for tuple struct and tuple variant constructors, such as `Some()`.

Before:
```rust
let a = &temp(); // Extended
let a = Some(&temp()); // Not extended :(
let a = Some { 0: &temp() }; // Extended
```

After:
```rust
let a = &temp(); // Extended
let a = Some(&temp()); // Extended
let a = Some { 0: &temp() }; // Extended
```

So, with this change, this works:

```rust
let a = Some(&String::from("hello")); // New: String lifetime now extended!

println!("{a:?}");
```

Until now, we did not extend through tuple struct/variant constructors (like `Some`), because they are function calls syntactically, and we do not want to extend the String lifetime in:

```rust
let a = some_function(&String::from("hello")); // String not extended!
```

However, it turns out to be very easy to distinguish between regular functions and constructors at the point where we do lifetime extension.

In practice, constructors nearly always use UpperCamelCase while regular functions use lower_snake_case, so it should still be easy to for a human programmer at the call site to see whether something qualifies for lifetime extension or not.

This needs a lang fcp.

---

More examples of what will work after this change:

```rust
let x = Person {
    name: "Ferris",
    job: Some(&Job { // `Job` now extended!
        title: "Chief Rustacean",
        organisation: "Acme Ltd.",
    }),
};

dbg!(x);
```

```rust
let file = if use_stdout {
    None
} else {
    Some(&File::create("asdf")?) // `File` now extended!
};

set_logger(file);
```

```rust
use std::path::Component;

let c = Component::Normal(&OsString::from(format!("test-{num}"))); // OsString now extended!

assert_eq!(path.components.first().unwrap(), c);
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. finished-final-comment-period The final comment period is finished for this PR / Issue. needs-fcp This change is insta-stable, or significant enough to need a team FCP to proceed. relnotes Marks issues that should be documented in the release notes of the next release. S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-lang Relevant to the language team
Projects
None yet
Development

Successfully merging this pull request may close these issues.