Skip to content

Conversation

@WaffleLapkin
Copy link
Member

@WaffleLapkin WaffleLapkin commented Oct 28, 2025

This is an extension to #147382.

With this PR Result<T, Uninhabited> and ControlFlow<Uninhabited, T> considered as must use iif T must be used.

For such cases the lint will mention that T is wrapped in a Result/ControlFlow with an uninhabited error/break.

The reasoning here is that Result<T, Uninhabited> is equivalent to T in which values can be represented and thus the must-used-ness should also be equivalent.

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Oct 28, 2025
@rustbot
Copy link
Collaborator

rustbot commented Oct 28, 2025

r? @fee1-dead

rustbot has assigned @fee1-dead.
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

@WaffleLapkin WaffleLapkin added A-lints Area: Lints (warnings about flaws in source code) such as unused_mut. I-lang-nominated Nominated for discussion during a lang team meeting. I-lang-easy-decision Issue: The decision needed by the team is conjectured to be easy; this does not imply nomination L-unused_must_use Lint: unused_must_use S-waiting-on-t-lang Status: Awaiting decision from T-lang relnotes Marks issues that should be documented in the release notes of the next release. labels Oct 28, 2025
Copy link
Member

@fee1-dead fee1-dead left a comment

Choose a reason for hiding this comment

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

@fee1-dead fee1-dead removed the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Oct 28, 2025
@traviscross traviscross added P-lang-drag-1 Lang team prioritization drag level 1. https://rust-lang.zulipchat.com/#narrow/channel/410516-t-lang T-lang Relevant to the language team needs-fcp This change is insta-stable, or significant enough to need a team FCP to proceed. and removed T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. I-lang-easy-decision Issue: The decision needed by the team is conjectured to be easy; this does not imply nomination labels Oct 29, 2025
@jackh726
Copy link
Member

So, the lang team discussed this in the triage meeting today. Overall sentiment was positive that this is probably something we want to do at some point, but there was one concern: people may rely on this today to ensure that people "observe" the state of the Ok variant, since we don't have a nice way today to mark that as must_use without making a newtype. So, to give a concrete example:

fn foo<E>() -> Result<u32, E>; // people may expect that you will check the value of the `u32`

I suggested that we could actually just get some real word data on this using crater. My thought was, we could do an experiment were we error on any function that (after monomorphization) returns Result<T, !> for any T that is not must_use (and maybe separately for any T that is must_use to get a counter set).

I do expect that this will result in a non-small amount of cases, but I think it might be a small enough set that we could browse through and get a sense of if this type of thing is relied upon.

Would you be interested in doing that experiment @WaffleLapkin? If not, I'm happy to help out and try that myself (or I can help if you do want to be involved).

@traviscross traviscross added I-lang-radar Items that are on lang's radar and will need eventual work or consideration. and removed I-lang-nominated Nominated for discussion during a lang team meeting. P-lang-drag-1 Lang team prioritization drag level 1. https://rust-lang.zulipchat.com/#narrow/channel/410516-t-lang labels Oct 29, 2025
@WaffleLapkin
Copy link
Member Author

WaffleLapkin commented Oct 29, 2025

@jackh726 I'd be happy to do the experiment. I might reach out for some help though ^^'

@jackh726
Copy link
Member

Sounds good! Let me know, happy to help if you run into issues.

@WaffleLapkin WaffleLapkin added the S-waiting-on-crater Status: Waiting on a crater run to be completed. label Nov 13, 2025
@WaffleLapkin WaffleLapkin force-pushed the never-worn-never-type branch from 17a892b to 69a5724 Compare December 2, 2025 10:14
@rustbot

This comment has been minimized.

@jackh726
Copy link
Member

jackh726 commented Dec 2, 2025

Copying this from #148577 (comment)

So, what's very clear is that this pattern comes in a lot of places. Definitely more than I would have expected coming into this. With this being said, I've gone through and opened a few crater results to see if I could spot any examples of @joshtriplett's concern, which was basically like "People may be relying on that Result<T, E> is always must_use to ensure that T is also must_use". The idea (from what I can try to summarize) was that perhaps there is some state in T that would be need to be observed in order to avoid subtle bugs.

So, going into this, I'm looking for types where the Ok variant seems to encode some "meta-state" that must be observed for correctness. There are obviously way too many examples for me to be exhaustive here. And, I have yet to find a situation that follows the pattern to be worried about.

That being said, given that this comes up so much, I think this decision is likely not as "light" as the lang team originally expected. Though, it's completely backwards- and forwards- compatible to make this change and undo later. Here a couple of examples of code I saw, though far from exhaustive.

no_std_net

pub trait ToSocketAddrs {
    type Iter: Iterator<Item = SocketAddr>;
    fn to_socket_addrs(&self) -> Result<Self::Iter, ToSocketAddrError>;
}
pub enum ToSocketAddrError {}

zino_http

let session_id = self
    .get_header("x-session-id")
    .or_else(|| self.get_header("session_id"))
    .and_then(|s| s.parse().ok()); // <- this parse relies `<String as FromStr>` which has `Err = Infallible`
ctx.set_session_id(session_id);

zerocopy

fn checked_shr(self, rhs: Self) -> Option<Self> { self.checked_shr(rhs.try_into().unwrap_or(u32::MAX)) }
                                                                                                     //^^^^^^^^ returns `Result<u32, Infallible>`

clap things

(this comes up a bunch, so just putting an example error, as I haven't dug into the actual pattern; it's also not super clear to me what the actual lint would be)

error: this type will no longer be must used: Result<std::string::String, Infallible>
  --> src/main.rs:72:9
   |
72 | /         /// A path to a project directory or a `Cargo.toml` file. If this is
73 | |         /// not provided, the current directory will be searched.
74 | |         #[clap(verbatim_doc_comment)]
75 | |         path: Option<String>,

…use lint

(or more accurately `Result<T, Uninhabited>`/`ControlFlow<Uninhabited, T>`).

This generalizes a previous change where we only did this for `T = ()`.
@WaffleLapkin WaffleLapkin removed the S-waiting-on-crater Status: Waiting on a crater run to be completed. label Dec 7, 2025
@jackh726
Copy link
Member

Going to renominate for lang, since we got some crater results and worth discussing.

@jackh726 jackh726 added the I-lang-nominated Nominated for discussion during a lang team meeting. label Dec 10, 2025
@traviscross traviscross added the P-lang-drag-1 Lang team prioritization drag level 1. https://rust-lang.zulipchat.com/#narrow/channel/410516-t-lang label Dec 10, 2025
@tmandry
Copy link
Member

tmandry commented Dec 10, 2025

@rfcbot fcp merge lang

I think we should merge this, for a few reasons.

  1. It's intuitive that the lint should work this way.
  2. It reduces noise of #[must_use] lints, leading people to pay more attention to them.
  3. For any case where this removes a lint where that removal was not desired, there has always been a more direct way to express your intent: Putting #[must_use] on the function.

We talked about this in the lang call and some people brought up that returning Result<u32, Uninhabited> could be used to make sure a caller looks at the u32. But we have #[must_use] on functions which should be the way that pattern is expressed.

For code that already returns Result<_, Uninhabited> it's true that this removes some cases that lint, but it's not overly likely that the author intended this; it's just as likely that this lint is noise. If the author did intend this they could have, and arguably, should have put #[must_use] on their function with a message to make the intent clear.

@scottmcm noticed that today, clippy lints on the case where you do this:

#[must_use]
fn foo() -> Result<i32, Uninhabited> { Ok(42) }
warning: this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]`
 --> src/lib.rs:2:1
  |
2 | fn foo() -> Result<i32, Uninhabited> {todo!()}
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = help: either add some descriptive message or remove the attribute
  = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.91.0/index.html#double_must_use
  = note: `#[warn(clippy::double_must_use)]` on by default

However, that lint is only if the #[must_use] attribute includes no message, and arguably the function should have a message to explain why the return value must be used.

While I think we should tweak this clippy lint so it no longer fires on Result<_, Uninhabited>, I don't think its existence should be enough to deter us from making this change now, because I don't think it will change anything substantially. This is the way I think we all expect must_use to work, and without changing it there's no way to create pressure for crates that happened to rely on an unintended consequence of its current behavior (which to my knowledge, we haven't actually found any examples of) to add #[must_use] to their functions.

@rust-rfcbot
Copy link
Collaborator

rust-rfcbot commented Dec 10, 2025

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

Concerns:

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.

@rust-rfcbot rust-rfcbot added proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. labels Dec 10, 2025
@traviscross
Copy link
Contributor

Agree with @tmandry.

@rfcbot reviewed

@joshtriplett
Copy link
Member

joshtriplett commented Dec 10, 2025

I think we should merge this as well. I also think we need to decide on a transition plan. That transition plan could be anything from "loud release notes" to "new lint", though the latter would need some thought on how to mark functions you don't want to add #[must_use] to.

Not filing this as a concern at this time, though.

@joshtriplett
Copy link
Member

However, one thing I will go ahead and file a concern on: today, if you try to add #[must_use] to a function returning Result<T, !> because you want to preserve the #[must_use] after this change, you'll get a clippy warning. People should not be in the position where old Rust complains about something needed for compatibility with the very next Rust version.

@rfcbot concern need-to-stop-warning-on-explicit-must_use-on-function-returning-Result-with-uninhabited

@WaffleLapkin
Copy link
Member Author

@joshtriplett currently clippy re-implements the is_must_use check, and does so incorrectly1 (yay fun). Would it be enough for your concern if clippy would be changed to use the function from rustc and then clippy behavior would be changed at the same time as this merging?

Footnotes

  1. it doesn't account for some updates that have happened in rustc

@Darksonn
Copy link
Contributor

However, one thing I will go ahead and file a concern on: today, if you try to add #[must_use] to a function returning Result<T, !> because you want to preserve the #[must_use] after this change, you'll get a clippy warning. People should not be in the position where old Rust complains about something needed for compatibility with the very next Rust version.

rfcbot concern need-to-stop-warning-on-explicit-must_use-on-function-returning-Result-with-uninhabited

Don't we have many examples of violating this? One that comes to mind right away is making &raw const (*union).field into a safe operation, which triggers warnings from unused unsafe blocks if you want to support the compiler prior to that change.

@scottmcm
Copy link
Member

Don't we have many examples of violating this?

We generally try to make it so there's a 1-release transition window. We don't need to worry about years-old versions, but we generally don't want to say that people have to update immediately because both things happen at once.

So for example, here we'd give people one release to add the function-level must_use where it's not warned on, then the next release change the lint to need it.

(Like how we stopped unneeded_unsafe warning on unsafe blocks in unsafe fn at least one release before insisting on them.)

@traviscross
Copy link
Contributor

cc @rust-lang/clippy @rust-lang/clippy-contributors

@rustbot
Copy link
Collaborator

rustbot commented Dec 13, 2025

This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-lints Area: Lints (warnings about flaws in source code) such as unused_mut. disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. I-lang-nominated Nominated for discussion during a lang team meeting. I-lang-radar Items that are on lang's radar and will need eventual work or consideration. L-unused_must_use Lint: unused_must_use needs-fcp This change is insta-stable, or significant enough to need a team FCP to proceed. P-lang-drag-1 Lang team prioritization drag level 1. https://rust-lang.zulipchat.com/#narrow/channel/410516-t-lang proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. relnotes Marks issues that should be documented in the release notes of the next release. S-waiting-on-t-lang Status: Awaiting decision from T-lang T-lang Relevant to the language team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

10 participants