#[must_use] for Result::Ok?

Imagine function like this:

fn foo(buf: &mut [u8]) -> Result<&[u8], Error> { ... }

It does something with the buffer data, potentially truncates it, and returns resulting slice. Unfortunately, it's quite easy for a user to make the following mistake:

foo(&mut buf)?;
// use `buf` instead of the return slice

While compiler lints against forgotten Result, we can not make it lint against forgotten Ok data.

We could write something like this:

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

fn foo(buf: &mut [u8]) -> Result<MustUse<&[u8]>, Error> { .. }

But it's not ideal from ergonomics perspective and may not be applicable in cases of already existing stable API.

Would it be possible to introduce something like #[must_use_variants] which would lint against unused variants in a return enum?

One example where it could be useful in std is the io::Read::read method. It's quite common, especially for beginners, to ignore the number of read bytes, which can result in subtle bugs.

1 Like

I wonder if it would be possible to change the behavior of #[must_use] to actually work with type statements. Currently it is quietly ignored with no warnings. There is an issue for that: #[must_use] silently does nothing on type aliases · Issue #26281 · rust-lang/rust · GitHub.

#[must_use]
type MustUse<T> = T;

fn foo(buf: &mut [u8]) -> Result<MustUse<&[u8]>, Error> { ... }

Alternatively, Rust could allow attributes on types. I don't see why this couldn't be valid syntactically.

fn foo(buf: &mut [u8]) -> Result<#[must_use] &[u8], Error> { ... }
4 Likes

Rather than eventually slapping #[must_use] on every possible variation of Rust code, wouldn't it make more sense to have the ability to introduce a scope-based warning to recognize when a non-!/() value is ignored via ;?

One of the wonderful features of Rust (besides alias control) is affine typing. This means that when you want to offer an API that disallows usage of old things, then that API must simply consume them. So instead of a reference to a slice you'd accept and return an owned value (or permission, i.e. a wrapper around a reference). #[must_use] seems all the rage these days, but it covers a niche and should stay there.

That's called https://doc.rust-lang.org/rustc/lints/listing/allowed-by-default.html#unused-results.

3 Likes