-
Notifications
You must be signed in to change notification settings - Fork 13.2k
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # 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: