Skip to content

Commit a099f17

Browse files
authored
Merge pull request #1940 from iopq/master
Updated RFC #886 with lessons learned from #1812
2 parents c379f5d + f4b6853 commit a099f17

File tree

1 file changed

+119
-0
lines changed

1 file changed

+119
-0
lines changed

text/1940-must-use-functions.md

+119
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
- Feature Name: none?
2+
- Start Date: 2015-02-18
3+
- RFC PR: https://github.com/rust-lang/rfcs/pull/1940
4+
- Rust Issue: https://github.com/rust-lang/rust/issues/43302
5+
6+
# Summary
7+
8+
Support the `#[must_use]` attribute on arbitrary functions, to make
9+
the compiler lint when a call to such a function is ignored. Mark
10+
`PartialEq::{eq, ne}` `#[must_use]` as well as `PartialOrd::{lt, gt, le, ge}`.
11+
12+
# Motivation
13+
14+
The `#[must_use]` lint is extremely useful for ensuring that values
15+
that are likely to be important are handled, even if by just
16+
explicitly ignoring them with, e.g., `let _ = ...;`. This expresses
17+
the programmers intention clearly, so that there is less confusion
18+
about whether, for example, ignoring the possible error from a `write`
19+
call is intentional or just an accidental oversight.
20+
21+
Rust has got a lot of mileage out connecting the `#[must_use]` lint to
22+
specific types: types like `Result`, `MutexGuard` (any guard, in
23+
general) and the lazy iterator adapters have narrow enough use cases
24+
that the programmer usually wants to do something with them. These
25+
types are marked `#[must_use]` and the compiler will print an error if
26+
a semicolon ever throws away a value of that type:
27+
28+
```rust
29+
fn returns_result() -> Result<(), ()> {
30+
Ok(())
31+
}
32+
33+
fn ignore_it() {
34+
returns_result();
35+
}
36+
```
37+
38+
```
39+
test.rs:6:5: 6:11 warning: unused result which must be used, #[warn(unused_must_use)] on by default
40+
test.rs:6 returns_result();
41+
^~~~~~~~~~~~~~~~~
42+
```
43+
44+
One of the most important use-cases for this would be annotating `PartialEq::{eq, ne}` with `#[must_use]`.
45+
46+
There's a bug in Android where instead of `modem_reset_flag = 0;` the file affected has `modem_reset_flag == 0;`.
47+
Rust does not do better in this case. If you wrote `modem_reset_flag == false;` the compiler would be perfectly happy and wouldn't warn you. By marking `PartialEq` `#[must_use]` the compiler would complain about things like:
48+
49+
```
50+
modem_reset_flag == false; //warning
51+
modem_reset_flag = false; //ok
52+
```
53+
54+
See further discussion in [#1812.](https://github.com/rust-lang/rfcs/pull/1812)
55+
56+
# Detailed design
57+
58+
If a semicolon discards the result of a function or method tagged with
59+
`#[must_use]`, the compiler will emit a lint message (under same lint
60+
as `#[must_use]` on types). An optional message `#[must_use = "..."]`
61+
will be printed, to provide the user with more guidance.
62+
63+
```rust
64+
#[must_use]
65+
fn foo() -> u8 { 0 }
66+
67+
68+
struct Bar;
69+
70+
impl Bar {
71+
#[must_use = "maybe you meant something else"]
72+
fn baz(&self) -> Option<String> { None }
73+
}
74+
75+
fn qux() {
76+
foo(); // warning: unused result that must be used
77+
Bar.baz(); // warning: unused result that must be used: maybe you meant something else
78+
}
79+
```
80+
81+
The primary motivation is to mark `PartialEq` functions as `#[must_use]`:
82+
83+
```
84+
#[must_use = "the result of testing for equality should not be discarded"]
85+
fn eq(&self, other: &Rhs) -> bool;
86+
```
87+
88+
The same thing for `ne`, and also `lt`, `gt`, `ge`, `le` in `PartialOrd`. There is no reason to discard the results of those operations. This means the `impl`s of these functions are not changed, it still issues a warning even for a custom `impl`.
89+
90+
# Drawbacks
91+
92+
This adds a little more complexity to the `#[must_use]` system, and
93+
may be misused by library authors (but then, many features may be
94+
misused).
95+
96+
The rule stated doesn't cover every instance where a `#[must_use]`
97+
function is ignored, e.g. `(foo());` and `{ ...; foo() };` will not be
98+
picked up, even though it is passing the result through a piece of
99+
no-op syntax. This could be tweaked. Notably, the type-based rule doesn't
100+
have this problem, since that sort of "passing-through" causes the
101+
outer piece of syntax to be of the `#[must_use]` type, and so is
102+
considered for the lint itself.
103+
104+
Marking functions `#[must_use]` is a breaking change in certain cases,
105+
e.g. if someone is ignoring their result and has the relevant lint (or
106+
warnings in general) set to be an error. This is a general problem of
107+
improving/expanding lints.
108+
109+
# Alternatives
110+
111+
- Adjust the rule to propagate `#[must_used]`ness through parentheses
112+
and blocks, so that `(foo());`, `{ foo() };` and even `if cond {
113+
foo() } else { 0 };` are linted.
114+
115+
- Should we let particular `impl`s of a function have this attribute? Current design allows you to attach it inside the declaration of the trait.
116+
117+
# Unresolved questions
118+
119+
- Should this be feature gated?

0 commit comments

Comments
 (0)