3

I can use the #[must_use] attribute to mark a function as having a return value that must either be used or explicitly ignored:

#[must_use]
fn plain() -> i32 {
  1
}

fn main() {
  plain();  // warning
}

However, if I want to change my function so it now returns a Result, I cannot do this:

#[must_use]
fn result() -> Result<i32, ()> {
  Ok(1)
}

fn main() {
  result().unwrap();
}

because the call to .unwrap() counts as a "use", and the #[must_use] attribute is applying to the entire Result.

Is there a way to make #[must_use] apply to the inner type as well? Essentially I would like to make sure that the i32 is not accidentally "eaten" by the semicolon.

I have the additional constraint that I cannot change the public API of this function, so I can't make it return a Result<MustUse<i32>, ()> or something along those lines.

cameron1024
  • 9,083
  • 2
  • 16
  • 36
  • _"I would like to make sure that the `i32` is not accidentally "eaten" by the semicolon"_ A means of [checking that the end of a function is never reached](https://stackoverflow.com/questions/62405398/how-to-statically-assert-the-end-of-a-function-is-unreachable) could help catch these. – E_net4 Apr 14 '22 at 15:04
  • 1
    _"I cannot change the public API of this function"_ But could you not create your own wrapper function? – E_net4 Apr 14 '22 at 15:05
  • If you estimate an API should return a inner must_use T, maybe you should ask them to do it. – Stargateur Apr 14 '22 at 15:08
  • For context, I own this function, but it has downstream crates that depend on this function. A breaking API change would require some coordination with the teams responsible for those crates. My suspicion is that what I'm asking for isn't currently possible, as I feel like it might require something like: `fn result() -> Result<#[must_use] i32, ()>`, which currently doesn't even parse – cameron1024 Apr 14 '22 at 16:11
  • if you ask me everything expect `()` and `!` should be "must_use". I doubt `fn result() -> Result<#[must_use] i32, ()>` will ever been implemented. Also, must_use attribute is already a little special in Rust ecosystem. – Stargateur Apr 14 '22 at 16:17
  • Uh, changing from `i32` to `Result` is already a breaking change. Why can't you also change `i32` into a wrapper type that has `#[must_use]` at the same time? – kmdreko Apr 14 '22 at 16:18
  • 1
    @kmdreko you have it wrong, the point of first snipped was to prove you can use must_use for i32 – Stargateur Apr 14 '22 at 16:37
  • Yeah at the very least, I think it would be better if functions (other than `()` and `!` returning functions) were `#[must_use]` by default, and maybe a `#[ignorable_return_value]` for commonly-discared values, but perhaps it's too late for that (though maybe an editiion could help) – cameron1024 Apr 15 '22 at 09:12

1 Answers1

5

You can't do this by directly adding your own annotation to the Result, but you can do this trivially with a newtype:

#[must_use]
#[repr(transparent)]
pub struct MustUse<T>(T);

impl<T> From<T> for MustUse<T> {
    fn from(v: T) -> Self {
        Self(v)
    }
}

impl<T> MustUse<T> {
    #[must_use]
    fn into_inner(self) -> T {
        self.0
    }
}

Now, given this declaration:

fn result() -> Result<MustUse<i32>, ()> {
  Ok(1.into())
}

All of the following cause a warning:

  • result(); warns because Result<_, _> is must-use.
  • result().unwrap(); warns because MustUse<_> is must-use.
  • result().unwrap().into_inner(); warns because MustUse<_>::into_inner() is must-use.

(Note that you can bypass the warning by invoking a different, non-must-use method of Result, such as result().ok(), but that will be true no matter what you do.)

cdhowie
  • 158,093
  • 24
  • 286
  • 300
  • This does get the ideal compiler output, but unfortunately coordinating a change from `Result` -> `Result, ()>` probably pushes it into the category of "not worth the effort" from an organizational standpoint. Seems like this is the best possible given where the language is right now – cameron1024 Apr 15 '22 at 09:14
  • Another way is to have alternative `unwrap()` that is marked `#[must_use]`. – Chayim Friedman Apr 15 '22 at 09:57