When to add #[must_use]
The #[must_use] attribute can be applied to types or functions when failing to explicitly consider them or their output is almost certainly a bug.
As an example, Result is #[must_use] because failing to consider it may indicate a caller didn't realise a method was fallible:
// Is `check_status` infallible? Or did we forget to look at its `Result`?
check_status();
Operators like saturating_add are also #[must_use] because failing to consider their output might indicate a caller didn't realise they don't mutate the left-hand-side:
// A caller might assume this method mutates `a`
a.saturating_add(b);
Combinators produced by the Iterator trait are #[must_use] because failing to use them might indicate a caller didn't realize Iterators are lazy and won't actually do anything unless you drive them:
// A caller might not realise this code won't do anything
// unless they call `collect`, `count`, etc.
v.iter().map(|x| println!("{}", x));
On the other hand, thread::JoinHandle isn't #[must_use] because spawning fire-and-forget work is a legitimate pattern and forcing callers to explicitly ignore handles could be a nuisance rather than an indication of a bug:
thread::spawn(|| {
// this background work isn't waited on
});
For reviewers
Look for any legitimate use-cases where #[must_use] will cause callers to explicitly ignore values. If these are common then #[must_use] probably isn't appropriate.
The #[must_use] attribute only produces warnings, so it can technically be introduced at any time. To avoid accumulating nuisance warnings though ping @rust-lang/libs for input before adding new #[must_use] attributes to existing types and functions.