rust-lang / rust-lang/rust-clippy

False positive in `needless_collect` with borrowing error

Open
#9,293 0 comments 3 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

C-bug I-false-positive I-suggestion-causes-error L-nursery
Dominant language
Rust
Stars
13.5k
Forks
2.2k
Avg merge
2d 10h
Merged PRs (30d)
32

Description

Summary

Inlining an iterator to a later call site whilst the iterator is accessed causes a compile error and potentially different program behaviour.

Lint Name

needless_collect

Reproducer

I tried this code:

fn main() {
    let prefix = 1;

    let mut children = [1, 2, 3, 4].into_iter();

    let prefix: Vec<_> = children.by_ref().take(prefix).collect();
    let suffix: Vec<_> = children.collect();

    let dummy = 0;

    let items = prefix
        .into_iter()
        .chain(std::iter::once(dummy))
        .chain(suffix);

    println!("{:?}", items.collect::<Vec<_>>())
}

I saw this happen:

warning: avoid using `collect()` when not needed
  --> src/main.rs:6:57
   |
6  |       let prefix: Vec<_> = children.by_ref().take(prefix).collect();
   |                                                           ^^^^^^^
...
11 |       let items = prefix
   |  _________________-
12 | |         .into_iter()
   | |____________________- the iterator could be used here instead
   |
   = note: `#[warn(clippy::needless_collect)]` on by default
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_collect
help: use the original Iterator instead of collecting it and then producing a new one
   |
6  ~     
7  |     let suffix: Vec<_> = children.collect();
 ...
10 | 
11 ~     let items = children.by_ref().take(prefix)
   |

I expected to see this happen:

No warning, applying the lint causes a compile error and further fixing the error causes the program's logic to change:

the fixed code would be:

fn main() {
    let prefix = 1;

    let mut children = [1, 2, 3, 4].into_iter();

    let suffix: Vec<_> = children.by_ref().collect();

    let dummy = 0;

    let items = children.by_ref().take(prefix)
        .chain(std::iter::once(dummy))
        .chain(suffix);

    println!("{:?}", items.collect::<Vec<_>>())
}

and prints:

[0, 1, 2, 3, 4]

but the original program would print:

[1, 0, 2, 3, 4]
Version
rustc 1.64.0-nightly (f8588549c 2022-07-18)
binary: rustc
commit-hash: f8588549c3c3d45c32b404210cada01e2a45def3
commit-date: 2022-07-18
host: x86_64-apple-darwin
release: 1.64.0-nightly
LLVM version: 14.0.6
Additional Labels

@rustbot label +I-suggestion-causes-error

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the needless_collect lint entry point and run the Rust reproducer from the issue. Trace why the suggestion moves collection across the borrowed iterator, then add regression coverage showing that no warning is emitted and the original program behavior is preserved.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
tooling
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.