rust-lang / rust-lang/rust-clippy
Suggest replacing simple loops with specialized folds
Nobody has claimed this yet.
- 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.collectfor 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
anyinstead of simple loops (that on condition match set a boolean to true and break) - clippy should suggest using
allinstead of simple loops (that on condition match set a boolean to false and break) - clippy should suggest using
findinstead of simple loops (that on condition match set an optional value to Some and break) - clippy should suggest using
position/rpositioninstead 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/productinstead of simple loops - ➡️ suggest using
contains(y)instead ofiter().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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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