rust-lang / rust-lang/rust-clippy

Suggest replacing simple loops with specialized folds

Open
#2,401 4 comments 13 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-lint
Dominant language
Rust
Stars
13.5k
Forks
2.2k
Avg merge
2d 10h
Merged PRs (30d)
32

Description

A lot of simple loops can be written using fold-like methods in iterators. Clippy should have a set of lints (maybe even a lint group[^1]) that promotes the more functional style.

Have a look at this code:

let xs = &[1, 2, 3, 4]; // <- ignore that this is a slice for a second

let mut res = vec![];
for x in xs {
    res.push(x);
}

Seasoned functional programmers recognize that is is just a fold/reduce. In Rust, you'd write it like this:

xs.iter().fold(vec![], |mut acc, x| { acc.push(x); acc })

Seasoned Rust programmers would use a specialized fold here (basically a shortcut that makes use of FromIterator):

let res: Vec<_> = xs.iter().collect();
  • clippy should suggest using .collect::<Vec<_>>() for loops that just build vectors

Let's make this a bit more complicated:

let xs = &[1, 2, 3, 4];

let mut res = vec![];
for x in xs {
    if *x > 2 { res.push(x); } 
}

This is still a pretty simple loop, and we can rewrite it like this:

let res: Vec<_> = xs.iter().filter(|&x| *x > 2).collect();

(Sadly, it's not a trivial as it first appeared to be because filter passes a reference to the closure, and we need to rewrite that in the comparison we copied from the if. Instead of |&x| *x > 2 we could also generate |x| **x > 2 or |&&x| x > 2.)

  • clippy should suggest using .filter.collect for these kinds of loops

There are of course a bunch more specialized folders we can recognize. For example:

let mut res = false;
for x in xs {
    if *x > 2 { res = true; } 
}

Isn't it wonderfully procedural? It takes 4 lines and inline mutation to find out if an item in that iterator is greater than two. How about suggesting any instead? (Also see this exisiting lint.)

let res = xs.iter().any(|x| *x > 2);
  • clippy should suggest using any instead of simple loops (that on condition match set a boolean to true and break)
  • clippy should suggest using all instead of simple loops (that on condition match set a boolean to false and break)
  • clippy should suggest using find instead of simple loops (that on condition match set an optional value to Some and break)
  • clippy should suggest using position/rposition instead of simple loops (that count iterations and on condition match store the current iteration number in a variable and break)
  • clippy should suggest using max{,_by,_by_key} instead of simple loops (that compare values and store larger one in variable)
  • clippy should suggest using min{,_by,_by_key} instead of simple loops (that compare values and store smaller one in variable)
  • clippy should suggest using sum/product instead of simple loops
  • ➡️ suggest using contains(y) instead of iter().any(|x| x == y) (#2534)

Haven't found an open issue about that, searched for "loop", "reduce", "fold".

[^1]: Please name the group after an Office Assistant!

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 by reviewing Clippy's existing unnecessary_fold lint and the loop patterns listed here, including collect/filter, any/all, find, position, min/max, sum, and product. Done means defining and implementing an agreed set of lints with suggestions for the supported patterns, including appropriate tests and any lint grouping.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
tooling
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.