Skip to content

needless_for_each is suggested when for_each actually improves performance over for #17856

Open

Description

@immconcon

Summary

There are a bunch if iterators where using for_each removes branching and improves performance: x..=y ranges, VecDeque iterator, .chain(), etc.
However, needless_for_each still triggers there, even though it leads to slower code.

The reproducer example is a little convoluted, since lint is a bit tricky to trigger; for simpler example with asm: (0..=n) godbolt.

P.S. If anything, I think the inverse rule should be true: for loops without control flow should be suggested to be rewritten as for_each.

Lint Name

needless_for_each

Reproducer

I tried this code:

pub fn bla(n: i32) {
    let mut v: std::collections::VecDeque<i32> = (0..=n).collect();
    if let Some(x) = v.pop_front() {
        v.push_back(x);
    }
    v.into_iter().for_each(|i| {
        println!("{i}");
    });
}

I saw this happen:

warning: needless use of `for_each`
 --> src/main.rs:6:5
  |
6 | /     v.into_iter().for_each(|i| {
7 | |         println!("{i}");
8 | |     });
  | |_______^
  |
  = help: for further information visit https://rust-lang.github.io/rust-clippy/main/index.html#needless_fo
r_each
  = note: `-W clippy::needless-for-each` implied by `-W clippy::pedantic`
  = help: to override `-W clippy::pedantic` add `#[allow(clippy::needless_for_each)]`
help: try
  |
6 ~     for i in v.into_iter() {
7 +         println!("{i}");
8 +     }
  |

Version

rustc 1.101.0-nightly (0abfedbc7 2026-10-02)
binary: rustc
commit-hash: 0abfedbc7cd4e725f126913880c95800394f7c37
commit-date: 2026-10-02
host: x86_64-unknown-linux-gnu
release: 1.101.0-nightly
LLVM version: 23.1.1

Additional Labels

No response

Activity

  1. added
    C-bugCategory: Clippy is not doing the correct thing
    I-false-positiveIssue: The lint was triggered on code it shouldn't have
    on Oct 7, 2026
  2. CommanderStorm commented on Oct 7, 2026

    @CommanderStorm
    Contributor

    -Copt-level=s gives same repro and less code on this one ^^
    To me this sounds a bit like rustc/LLVM has a hard time reasoning about the inclusive ranges (since issue does not occur with the exclusive range 0..n).

    There are a few issues in rust which seem similar -> https://github.com/rust-lang/rust/issues?q=is%3Aissue+state%3Aopen+for_each+label%3AI-slow

    Maybe closest issue (but not quite?). This one seems a bit more narrow and better to understand.
    -> rust-lang/rust#112911

    Would you be open to opening an issue with https://github.com/rust-lang/rust too? ^^

  3. immconcon commented on Oct 8, 2026

    @immconcon
    Author

    There are a few issues in rust which seem similar -> https://github.com/rust-lang/rust/issues?q=is%3Aissue+state%3Aopen+for_each+label%3AI-slow

    Maybe closest issue (but not quite?). This one seems a bit more narrow and better to understand. -> rust-lang/rust#112911

    Would you be open to opening an issue with https://github.com/rust-lang/rust too? ^^

    Sorry, I should've explained the problem better. The problem lies in what for x in y desugars into:

    for x in y {}
    // roughly translates to this:
    let mut $iter = y.into_iter();
    while let Some(x) = $iter.next() {}

    However, fold and for_each can be optimized better than simply calling next for every item. Compare next() vs fold() in VecDeque. Or next and fold in .chain() iterator. Iterators for ..= ranges are optimized through try_fold() -> Result<B, !>, so it is harder to reason about, but I think two previous examples are good enough.
    This is not fixable from compiler side without breaking code that relies on particular for desugaring.
    So no, I don't think I will be opening issue with rust-lang/rust.

    -Copt-level=s gives same repro and less code on this one ^^ To me this sounds a bit like rustc/LLVM has a hard time reasoning about the inclusive ranges (since issue does not occur with the exclusive range 0..n).

    Yeah it's probably better. Although I'm a little cautious of optimizing for binary size over default -Copt-level=3.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-bugCategory: Clippy is not doing the correct thingI-false-positiveIssue: The lint was triggered on code it shouldn't have

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions