Skip to content

Add a consideration for must_use #9

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 8, 2021
Merged
Show file tree
Hide file tree
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
1 change: 1 addition & 0 deletions src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
39 changes: 39 additions & 0 deletions src/code-considerations/design/must-use.md
Original file line number Diff line number Diff line change
@@ -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.