Skip to content
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

Lint against Iterator::map receiving a callable that returns () #106991

Open
estebank opened this issue Jan 17, 2023 · 4 comments · Fixed by #107890
Open

Lint against Iterator::map receiving a callable that returns () #106991

estebank opened this issue Jan 17, 2023 · 4 comments · Fixed by #107890
Labels
A-diagnostics Area: Messages for errors, warnings, and lints A-lint Area: Lints (warnings about flaws in source code) such as unused_mut. D-newcomer-roadblock Diagnostics: Confusing error or lint; hard to understand for new users. D-papercut Diagnostics: An error or lint that needs small tweaks. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@estebank
Copy link
Contributor

estebank commented Jan 17, 2023

Code

fn foo(items: &mut Vec<u8>){
    items.sort();
}

fn main() {
    let mut x: Vec<Vec<u8>> = vec![
        vec![0, 2, 1],
        vec![5, 4, 3],
    ];
    x.iter_mut().map(foo);
    println!("{x:?}");
}

Current output

Passes

Desired output

warning: `Iterator::map` call that discard the iterator's values
   |
LL | fn foo(items: &mut Vec<u8>) {
   |    --- this function returns `()`, which is likely not what you wanted
...
LL |     x.iter_mut().map(foo)
   |                  --- ^^^ called `Iterator::map` with callable that returns `()`
   |                  |
   |                  after this call to map, the resulting iterator is `impl Iterator<Item = ()>`, which means the only information carried by the iterator is the number of items
   |
   = note: `Iterator::map`, like many of the methods on `Iterator`, gets executed lazily, meaning that its effects won't be visible until it is iterated
help: you might have meant to use `Iterator::for_each`
   |
LL -     x.iter_mut().map(foo)
LL +     x.iter_mut().for_each(foo)
   |

Rationale and extra context

Mapping to () is almost always a mistake. The for_each suggestion should only be emitted if it would make sense with foo, like if it modifies the argument or has side-effects like printing or logging (the later would be hard to check for).

Other cases

No response

Anything else?

No response

@estebank estebank added A-diagnostics Area: Messages for errors, warnings, and lints T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. A-lint Area: Lints (warnings about flaws in source code) such as unused_mut. D-papercut Diagnostics: An error or lint that needs small tweaks. D-newcomer-roadblock Diagnostics: Confusing error or lint; hard to understand for new users. labels Jan 17, 2023
@matthiaskrgr
Copy link
Member

matthiaskrgr commented Jan 17, 2023

Like this? 😄 https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unit_fn

edit: ah this might only lint options but maybe not too hard to expand?

@estebank
Copy link
Contributor Author

@matthiaskrgr yes 😅

I'd like to uplift it so that it still triggers even in the face of other errors. If you run clippy on https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=51211f1c12a2b1995b24bab269a02ce0, it will only complain about the return type.

@obeis
Copy link
Contributor

obeis commented Feb 4, 2023

@rustbot claim

compiler-errors added a commit to compiler-errors/rust that referenced this issue Feb 25, 2023
Lint against `Iterator::map` receiving a callable that returns `()`

Close rust-lang#106991
compiler-errors added a commit to compiler-errors/rust that referenced this issue Feb 25, 2023
Lint against `Iterator::map` receiving a callable that returns `()`

Close rust-lang#106991
@bors bors closed this as completed in fa10a21 Feb 26, 2023
@estebank
Copy link
Contributor Author

I'm reopening the ticket to track the comment in this one case:

https://github.com/rust-lang/rust/pull/107890/files#diff-0e3b31971aaa3e0b1e633c38a8c5ea34fcc99b7e96b12f61bf3b53820213f86e

fn foo(items: &mut Vec<u8>) {
    items.sort();
}

fn bar() -> impl Iterator<Item = i32> {
    //~^ ERROR expected `foo` to be a fn item that returns `i32`, but it returns `()` [E0271]
    let mut x: Vec<Vec<u8>> = vec![vec![0, 2, 1], vec![5, 4, 3]];
    x.iter_mut().map(foo)
}

fn main() {
    bar();
}

should provide more context than

error[E0271]: expected `foo` to be a fn item that returns `i32`, but it returns `()`
  --> $DIR/issue-106991.rs:5:13
   |
LL | fn bar() -> impl Iterator<Item = i32> {
   |             ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found `i32`
   |
   = note: required for `Map<std::slice::IterMut<'_, Vec<u8>>, for<'a> fn(&'a mut Vec<u8>) {foo}>` to implement `Iterator`

@estebank estebank reopened this Feb 27, 2023
@obeis obeis removed their assignment Jun 3, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-diagnostics Area: Messages for errors, warnings, and lints A-lint Area: Lints (warnings about flaws in source code) such as unused_mut. D-newcomer-roadblock Diagnostics: Confusing error or lint; hard to understand for new users. D-papercut Diagnostics: An error or lint that needs small tweaks. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants