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
66 changes: 66 additions & 0 deletions text/0000-derive-must-use.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
- Feature Name: Let `Option` derive `#[must_use]`
- Start Date: 2026-01-07
- RFC PR: [rust-lang/rfcs#3906](https://github.com/rust-lang/rfcs/pull/3906)
- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000)

# Summary
[summary]: #summary

Let `Option` and `Box` derive `#[must_use]` from their generic parameter `T`.

# Motivation
[motivation]: #motivation

If we write:
```rust
#[must_use]
struct Redraw;

fn do_thing() -> Option<Redraw> {
// Do some thing which requires a redraw...
Some(Redraw)
}
```
then `do_thing` should be `#[must_use]`, and while we can apply the `#[must_use]` attribute to the function `do_thing`, we shouldn't have to (remember to do so).

# Guide-level explanation
[guide-level-explanation]: #guide-level-explanation

The `Option` and `Box` types will "magically" have the `#[must_use]` attribute if and only if their generic parameter `T` does.
Copy link

Choose a reason for hiding this comment

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

why "magically"?

why not permit #[must_use] on the parameters themselves, e.g.

pub struct Option<#[must_use] T> {
    Some(T),
    None,
}

"desugars"1 to

impl<T: MustUse> MustUse for Option<T> {}

Footnotes

  1. There will not necessarily be an actual MustUse trait in the implementation.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

impl<T: MustUse> MustUse for Option {}

#[must_use] is not currently a trait. If you want to make an RFC for that, please go ahead. It may be a good idea, but it is an idea for another RFC, not this one.

Since #[must_use] is not currently a trait, we lack the language to describe derive behaviour, hence use of "magically" here.

Option<#[must_use] T>

Use of #[must_use] on parameters is novel syntax that I don't particularly like.

Copy link

Choose a reason for hiding this comment

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

sorry, i thought i was clear in the original comment that "the MustUse trait" was mentioned purely as an analogy, and not as a normative suggestion, but perhaps not. fwiw i think a MustUse trait would be bad since

  1. trait solver is slow and heavy compared to just looking at some simple hard-coded rules based on named types/traits.
  2. it gives perhaps too much flexibility to the user. do we really want people to be able to write impl<T: Copy> MustUse for Thing<T>, for instance.

i think users should have access to more fine-grained #[must_use] annotations, but that doesn't per-se have to be part of this change since Option and Box alone would be clear usability improvements in my book.

i don't want to overlitigate, but i still reckon this RFC doesn't really justify the "magic" design as written: it's clearly conceptually easier to special case these two generic types, but i don't see it as better from a maintenance perspective if we do want to rip it out for something holistic. for instance is_ty_must_use is already more complicated than ideally it should be (sidebar: and, seemingly, incomplete; see this playground). should we keep adding additional branches ad infinitum?

Copy link
Member

Choose a reason for hiding this comment

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

Use of #[must_use] on parameters is novel syntax that I don't particularly like.

Attribute on type parameter is already in use since #3621 (ref), it is no longer a novel syntax.

#[derive(CoercePointee)]
#[repr(transparent)]
struct MySmartPointer<#[pointee] T: ?Sized, U> {
    ptr: Box<T>,
    _phantom: PhantomData<U>,
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Option<#[must_use] T>

Something like this would be clearer (though I do find derive's "magic" handling of generic parameters unfortunate relative to making this explicit, as with autoimpl):

#[derive(must_use)]
enum Option<T> { /* ... */ }

That may be besides the point though if this RFC adds more complexity than value. (I had no idea that Result<T, Infallible> would not trigger a "must use" warning.)


# Reference-level explanation
[reference-level-explanation]: #reference-level-explanation

This will be an internal detail of the standard library. It may use another special attribute like `#[derive_must_use_from(T)]`, but for the purposes of this RFC, the `derive_must_use_from` attribute may remain unstable forever.

# Drawbacks
[drawbacks]: #drawbacks

I see no drawbacks besides the small amount of complexity involved.

# Rationale and alternatives
[rationale-and-alternatives]: #rationale-and-alternatives

The only obvious (non-empty) alternative is to add (and stabilise) a new `#[derive_must_use_from(T)]` attribute and apply this to `Option<T>` (and [other types](#future-possibilities)).

This would not be a strict alternative in that nothing prevents this from being done later.

# Prior art
[prior-art]: #prior-art

[RFC #3737](https://github.com/rust-lang/rfcs/pull/3737) is vaguely related (only in that it also pertains to `#[must_use]`).

`#[must_use`] is already tracked through tuples ([example](https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=488fc81eba51a4aded6faeab7ee9bf44)), though strictly speaking this does not apply `#[must_use]` to the tuple type.

# Unresolved questions
[unresolved-questions]: #unresolved-questions

# Future possibilities
[future-possibilities]: #future-possibilities

Possibly a few other standard library types would benefit from this derivation of `#[must_use]`:

- `Box<T>` can do so (included in this RFC, though motivation is weaker)
- `RefCell<T>` and `Mutex<T>` *could* do so but it is unlikely of any use
- `Rc<T>`, `Arc<T>` and various reference types *should not* since they do/may not have exclusive ownership of the value
- `Vec<T>` and other containers *could* do so