-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
discussion/tracking issue for #[must_use]
functions in the standard library
#48926
Comments
I'm not sure "result-like" is broad enough. Maybe something like "things that may have side effects, but which ought never be used for their side effects, especially if often expensive". As a canonical example of such a thing, I definitely don't think this needs to block stabilization, because any |
Add #[must_use] to a few standard library methods Chosen to start a precedent of using it on ones that are potentially-expensive and where using it for side effects is particularly discouraged. Discuss :) ```rust warning: unused return value of `std::iter::Iterator::collect` which must be used: if you really need to exhaust the iterator, consider `.for_each(drop)` instead --> $DIR/fn_must_use_stdlib.rs:19:5 | LL | "1 2 3".split_whitespace().collect::<Vec<_>>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused return value of `std::borrow::ToOwned::to_owned` which must be used: cloning is often expensive and is not expected to have side effects --> $DIR/fn_must_use_stdlib.rs:21:5 | LL | "hello".to_owned(); | ^^^^^^^^^^^^^^^^^^^ warning: unused return value of `std::clone::Clone::clone` which must be used: cloning is often expensive and is not expected to have side effects --> $DIR/fn_must_use_stdlib.rs:23:5 | LL | String::from("world").clone(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ``` cc rust-lang#48926
Add #[must_use] to a few standard library methods Chosen to start a precedent of using it on ones that are potentially-expensive and where using it for side effects is particularly discouraged. Discuss :) ```rust warning: unused return value of `std::iter::Iterator::collect` which must be used: if you really need to exhaust the iterator, consider `.for_each(drop)` instead --> $DIR/fn_must_use_stdlib.rs:19:5 | LL | "1 2 3".split_whitespace().collect::<Vec<_>>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused return value of `std::borrow::ToOwned::to_owned` which must be used: cloning is often expensive and is not expected to have side effects --> $DIR/fn_must_use_stdlib.rs:21:5 | LL | "hello".to_owned(); | ^^^^^^^^^^^^^^^^^^^ warning: unused return value of `std::clone::Clone::clone` which must be used: cloning is often expensive and is not expected to have side effects --> $DIR/fn_must_use_stdlib.rs:23:5 | LL | String::from("world").clone(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ``` cc rust-lang#48926
Add #[must_use] to a few standard library methods Chosen to start a precedent of using it on ones that are potentially-expensive and where using it for side effects is particularly discouraged. Discuss :) ```rust warning: unused return value of `std::iter::Iterator::collect` which must be used: if you really need to exhaust the iterator, consider `.for_each(drop)` instead --> $DIR/fn_must_use_stdlib.rs:19:5 | LL | "1 2 3".split_whitespace().collect::<Vec<_>>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused return value of `std::borrow::ToOwned::to_owned` which must be used: cloning is often expensive and is not expected to have side effects --> $DIR/fn_must_use_stdlib.rs:21:5 | LL | "hello".to_owned(); | ^^^^^^^^^^^^^^^^^^^ warning: unused return value of `std::clone::Clone::clone` which must be used: cloning is often expensive and is not expected to have side effects --> $DIR/fn_must_use_stdlib.rs:23:5 | LL | String::from("world").clone(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ``` cc rust-lang#48926
As of #49533, this is now on |
I think the non-assigning arithmetic functions are good candidates for this (see #50124). |
Another metric could be: "things that (are highly likely to) allocate". |
There's a concrete question around |
A thought: I agree we don't want to clutter code with |
I was starting to think down these lines: Any function which:
And then we opt-out of things with internal mutability? We may still end up with a lot of attributes if we follow @Manishearth's suggestion to have explanatory text on every function. (Not sure if I agree with that or not). |
The Rust language already has a concept of side-effect freedom which is MUST is a very strong word. When I see |
Hmm, I'm not sure that warning on unused |
HashMap::new doesn't allocate. |
Sorry, you're right. I guess |
I've made an implementation of my lint idea here: #50805 @jonhoo allocating functions can be Edit: fixed link |
I've opened an RFC now as it seems that it requires one: rust-lang/rfcs#2450 |
As discussed in #52201, I think another set of candidates for this is methods like |
Just to give another example: I just noticed a bug in code that I wrote which could have been caught early with a proper vec.pop(); I didn't think about what would happen if the I think marking |
I recently watched a newcomer to Rust write this shape of code: let mut s = String::new();
stdin.read_line(&mut s);
s.trim();
if s == "hello" { /* ... */ } They either believed that |
I think I would lean more toward In contrast, something like calling |
@Jasperav That might be too big a change to make except at an edition boundary, but Rust 2021 is right around the corner 😃 |
It would also be appropriate to create an off-by-default clippy lint implementing that behavior, I think. |
No need for a clippy lint; it already exists in rustc: https://doc.rust-lang.org/rustc/lints/listing/allowed-by-default.html#unused-results |
FYI, I've added a |
I like that change. I'd support a guideline for that kind of thing, perhaps
That also covers things like the attribute that was added on |
Is there any progress towards clearer guidelines for |
I propose the following guideline:
I disagree with the following:
"must" does not indicate the severity of the bug; It indicates the level of certainty that not using the value is wrong or sub-optimal. I believe with this guideline, |
After thinking some more, I think I can do better. I see three distinct reasons for adding
To say more about those 3 conditions:
Looking at the 3 conditions from another perspective:
Condition #2 seems to stand out as "not as severe". But personally I don't think this is something to get hung up on - any Bonus condition #4: The return value is literally designed to determine control flow (looking at you, It might be nice to have a special annotation for #1 like |
@m-ou-se No, I don't know of any. It really needs someone to pick it up and find a "hits the majority" subset so that
Yeah, exactly. But because it's so common -- and the downside to not using something is often so minor -- I think this is a place for a lint to heuristically do it. We know that For example, we could start with a lint for any We could also consider an |
Looking at this from the other direction, manually annotating a bunch of functions that fit into these categories (whatever they may be) could be a way to gauge the efficacy of any proposed heuristic. Effectively, this would be like how lifetime elision was established based on feedback from real world usages. |
@camsteffen just brought this discussion to my attention. Recently I independently asked around on Zulip and IRLO about adding With that go ahead, I put together a list of around 800 changes and have been working them off bit by bit. I'm about halfway done at this point. You can see the tracking issue here: I'd appreciate everyone's feedback as I continue working on this. |
It looks like your work is focused on the "pure function" case, which is the most straightforward. |
Done! The MCP was accepted and the tracking issue is closed. |
The MCP adds clear guidelines for some cases that should have |
I concur. The guidelines in the MCP were for what I was using to scope my changes. I did not intend them to be proscriptive. There are certainly more cases that should be added. I fudged my own rules a bit, even. To wit, I added a number of functions that take and return HashMap::key_mut(&mut self) -> &mut K;
HashMap::get_mut(&mut self) -> &mut V;
HashMap::get_key_value(&mut self) -> (&K, &V);
HashMap::get_key_value_mut(&mut self) -> (&mut K, &mut V); |
If a semicolon discards the result of a function or method tagged with `#[must_use]`, the compiler will emit a lint message warning that the return value is expected to be used. This lint suggests adding `#[must_use]` to public functions that: - return something that's not already marked as `must_use` - have no mutable *reference* args (having a mutable owned arg is fine, since as the method took ownership, the caller can only access the result via the returned self, so it therefore still must be used) - don't mutate statics Docs: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute https://rust-lang.github.io/rust-clippy/master/index.html#must_use_candidate For more on best practices of when to use `must_use`, see: rust-lang/rust#48926 (comment) Fixes #57. GUS-W-10222390.
If a semicolon discards the result of a function or method tagged with `#[must_use]`, the compiler will emit a lint message warning that the return value is expected to be used. This lint suggests adding `#[must_use]` to public functions that: - return something that's not already marked as `must_use` - have no mutable *reference* args (having a mutable owned arg is fine, since as the method took ownership, the caller can only access the result via the returned self, so it therefore still must be used) - don't mutate statics Docs: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute https://rust-lang.github.io/rust-clippy/master/index.html#must_use_candidate For more on best practices of when to use `must_use`, see: rust-lang/rust#48926 (comment) Fixes #57. GUS-W-10222390.
RFC 1940 authorized use of the
#[must_use]
attribute on functions (with the result that invocations of the function behave as if the return type was#[must_use]
.This has been implemented for a while under a
fn_must_use
feature gate (tracking issue), and PR #48925 proposes stabilization.#[must_use]
has been added to the comparison methods in the standard library (.eq
,.lt
, &c.), but we never got a consensus as to what other standard library methods, if any, should get it. In principle, any function or with no side effects could be must-use (e.g.,.len()
on collections), but adding dozens or hundreds of annotations to the standard library feels kind of dramatic: perhaps must-use should instead be reserved for functions with "unusually result-like" semantics—after all, users who want to be really sure they're not uselessly throwing away a return value can always opt in to the (allow-by-default)unused-results
lint.If we wanted to be very conservative, we could refuse to stabilize until we've made a decision on this: if we were to stabilize first, any new
#[must_use]
annotations in the standard library would be insta-stable. But, maybe this isn't a big deal (cargo
passescap-lints allow
to dependencies, so tinkering with lints shouldn't break people's builds).The text was updated successfully, but these errors were encountered: