diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 4bc67bb..68dafbd 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -19,6 +19,7 @@ - [Code considerations](./code-considerations/summary.md) - [Design](./code-considerations/design/summary.md) - [Public APIs](./code-considerations/design/public-apis.md) + - [When to add `#[must_use]`](./code-considerations/design/must-use.md) - [Breaking changes](./code-considerations/breaking-changes/summary.md) - [Breakage from changing behavior](./code-considerations/breaking-changes/behavior.md) - [Breakage from new trait impls](./code-considerations/breaking-changes/new-trait-impls.md) diff --git a/src/code-considerations/design/must-use.md b/src/code-considerations/design/must-use.md new file mode 100644 index 0000000..588c8c6 --- /dev/null +++ b/src/code-considerations/design/must-use.md @@ -0,0 +1,39 @@ +# 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: + +```rust +// 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: + +```rust +// 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 `Iterator`s are lazy and won't actually do anything unless you drive them: + +```rust +// 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: + +```rust +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.