Skip to content
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

Make Option<T> #[must_use] if T is #71368

Open
ctiller opened this issue Apr 20, 2020 · 12 comments
Open

Make Option<T> #[must_use] if T is #71368

ctiller opened this issue Apr 20, 2020 · 12 comments
Labels
A-async-await Area: Async & Await AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. P-medium Medium priority T-lang Relevant to the language team, which will review and decide on the PR/issue.

Comments

@ctiller
Copy link

ctiller commented Apr 20, 2020

I was recently refactoring some code that originally looked like this (minimized):

fn foo() {}
async fn bar() {
  Some(foo());
}

bar() was far away from the definition of foo().
I had need to refactor the code so that foo was also async, and so now I had:

async fn foo() {}
async fn bar() {
  Some(foo());
}

This gives no compiler diagnostic and instead the body of foo is not executed.

@jonas-schievink
Copy link
Contributor

Why do you create an option and do nothing with it?

@ctiller
Copy link
Author

ctiller commented Apr 20, 2020

did I minimize this example too far?... actual code was closer to:

some_option.map(|value| foo(value))

@tmandry
Copy link
Member

tmandry commented Apr 20, 2020

The issue is that Option<T> is not #[must_use] even when T is. We can't change this backwards compatibly, but perhaps adding a warning is possible.

@jonas-schievink
Copy link
Contributor

Also see #67387

(#[must_use] is just a warning, so it can be added without breaking code)

@tmandry tmandry added A-async-await Area: Async & Await T-lang Relevant to the language team, which will review and decide on the PR/issue. labels Apr 20, 2020
@nikomatsakis
Copy link
Contributor

Nominating for @rust-lang/lang consideration. This is another case where extending #[must_use] makes sense, I think -- in particular, an Option<impl Future> should warn when it is unused, just as a impl Future does.

I was pondering why it makes sense, and I think the reason is pretty clear -- an Option contains its T in a public field and doesn't in any sense try to "abstract over it" or "encapsulate it" (which were some of the reasons that we were reluctant to extend #[must_use] uniformly across all types).

@tmandry tmandry added the P-medium Medium priority label Apr 21, 2020
@tmandry
Copy link
Member

tmandry commented Apr 21, 2020

From wg-async-foundations triage: marking On Deck, because forgetting to await is a fairly common thing, and part of the learnability story for async/await is making sure you don't do that.

@tmandry tmandry added the AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. label Apr 21, 2020
@joshtriplett
Copy link
Member

How feasible would it be to detect taking a #[must_use] value and putting it in a value that subsequently never gets touched? That might give far less false positives than trying to make Option<impl Future> a #[must_use] type.

@ecstatic-morse
Copy link
Contributor

ecstatic-morse commented Apr 23, 2020

What about making the enum variant constructors themselves #[must_use]? In this case that would be Option::Some (the function that returns an Option, not the variant itself).

@nikomatsakis
Copy link
Contributor

Marking the variant constructor must_use seems like it would have too much fallout, since it would mean that code like Ok(22); would warn as well, wouldn't it?

Also, I think we might well want to warn on code like the following:

fn foo() -> Option<impl Future> { ... }

fn bar() {
   foo();
}

I'm not quite I understand what @joshtriplett is proposing. It sounds like saying "if you construct a enum or struct variant that contains a #[must_use] value, and that struct is unused". I'm reluctant to do that, because it implies that (a) it would apply to all enum/struct types, or at least "most", and we've historically felt that is too broad (which I still think is true), and also (b) it doesn't address cases like foo above, although I think those cases are a bit more borderline than the original example.

I'd like to ask: what is the resistance to making Option<T> be "must-use" if T is must-use? I guess it would be worth doing a bit of a crater run to see the effects before making a final decision, since it may be that cases like None; raise a bunch of false positives, but in general it seems ok to extend "must-use" to wrapper types like Option that don't "encapsulate" their contents.

@gebressler
Copy link

I support making Option<T>``#[must_use] if T is.

In https://fuchsia-review.googlesource.com/c/fuchsia/+/501960/9/src/sys/component_manager/src/capability.rs#173, I wanted to have a trait method returning a Result of Option:

trait Foo {
    fn bar(&self) -> Result<Option<MustUseType>, Error>;
}

I wanted Option<MustUseType> to be #[must_use]. Result itself is #[must_use], but this doesn't help because that only applies to the Result, which is "used" as soon as the ? operator is invoked on it. If this fn were directly returning Option<MustUseType>, I could have marked the fn #[must_use], but I need to be able to return an error. In the end I resorted to creating a custom wrapper type for Option<MustUseType>.

@tmandry tmandry changed the title Async execution can get lost during refactoring Make Option<T> #[must_use] if T is Mar 18, 2021
@ActuallyHappening
Copy link

ActuallyHappening commented May 17, 2023

I think Option should be #[must_use], just came across a situation where I called a helper func returning an Option with Option<Error> but forgot to actually ? the error, no lint warning.

Actually checked the std definition for a #[must_use] declaration confused, but wasn't there.

@vultix
Copy link

vultix commented Mar 8, 2024

Not sure if this should be in another issue, but I'd like this to be supported for types other than Options.

I'm writing an actor library, where sending a message to an actor returns an ActorResp<T> type (a wrapper around a oneshot channel).

I'd love to be able to mark it as #[must_use_if(T: must_use)] or something similar. Makes me wonder if we should change must_use to a marker trait.

impl<T: MustUse> MustUse for ActorResp<T> {
    const REASON: &'static str = "This ActorResp resolves to type that must be used";
}

With some more nightly features, this could lead to very nice messages:

impl<T: MustUse> MustUse for ActorResp<T> {
    const REASON: &'static str = const_format!("This ActorResp resolves to `{}`, which must be used: {}", type_name::<T>(), T::REASON);
}

matta added a commit to matta/my-rust-chat-server that referenced this issue May 22, 2024
This allows us to mark ActionOption as
`#[must_use]`, which helps prevent ignoring
actions accidentally (e.g. by forgetting to handle
a return value).  See
rust-lang/rust#71368 for
more context.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-async-await Area: Async & Await AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. P-medium Medium priority T-lang Relevant to the language team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

9 participants