Skip to content

Lint for fold closure that never moves the accumulator #6053

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

Open
scottmcm opened this issue Sep 16, 2020 · 3 comments
Open

Lint for fold closure that never moves the accumulator #6053

scottmcm opened this issue Sep 16, 2020 · 3 comments
Assignees
Labels
A-lint Area: New lints good first issue These issues are a good way to get started with Clippy L-perf Lint: Belongs in the perf lint group

Comments

@scottmcm
Copy link
Member

scottmcm commented Sep 16, 2020

What it does

Lint when a fold closure always returns the accumulator from the input, having only used it by reference. (As opposed to consuming the accumulator, or returning something else.)

Categories (optional)

  • Kind: perf

The compiler currently cannot always optimize away passing along the accumulator every time (see rust-lang/rust#76725), so it's better to not do that if it's actually the same thing every time anyway.

Drawbacks

None.

(Well, it leaves something mut, but that's easily fixable with let v = v;.)

Example

    let word = word.to_lowercase();
    let char_count: HashMap<char, usize> = word.chars().fold(HashMap::new(), |mut chars, c| {
        *chars.entry(c).or_default() += 1;
        chars
    });

Could be written as:

    let word = word.to_lowercase();
    let mut char_count: HashMap<char, usize> = HashMap::new();
    word.chars().for_each(|c| *chars.entry(c).or_default() += 1);
@scottmcm scottmcm added the A-lint Area: New lints label Sep 16, 2020
@workingjubilee
Copy link
Member

The somewhat odd

let x = {  let mut y = Vec::new();
    /* Do the mutation. */
    y
};

is also an option over binding and rebinding.

@camsteffen camsteffen added L-perf Lint: Belongs in the perf lint group good first issue These issues are a good way to get started with Clippy labels Feb 7, 2021
@dahlbaek
Copy link

dahlbaek commented Jan 3, 2025

I ran into this when working on an Advent of Code solution and was surprised that I could increase throughput for one of my solutions by 40% by just using a for_each instead of a fold 🤯 . However, looking at this with a data pipeline mindset (think apache spark), where I think Rust is gaining traction, isn't it worth considering the fact that a chain of iterator methods that end in a fold(...) can be continued directly with an iter(), whereas a chain that ends in a for_each cannot be continued? In data pipeline style code, that can be pretty bad for readability in my opinion.

For example, consider

collection
    .map(...)
    .fold(HashMap::new(), ...) // group_by/reduce_by_key operation
    .flat_map(...)
    .fold(HashMap::new(), ...) // group_by/reduce_by_key operation
    .map(...)
    .sum()

Switching this over to the for_each style will trash the flow of the code and thereby the readability (in my opinion).

Maybe it would be worth it to consider a name such as collect_with for what was called fold_mut in rust-lang/rust#76725, thereby somewhat giving it the role of a "custom builder" function rather than a niche fold variant? If such a collect_with was included in the std lib, I suppose one could put a direct suggestion in this lint to use collect_with instead of fold.

I note that the name collect_with was considered (and discarded) in rust-lang/rust#92982, but the collect_with that was considered there did not serve the purpose of an ad-hoc collect function (it didn't take a closure to specify how to build the collection).

Such a collect with could be implemented simply with

    fn collect_with<B, F>(self, mut init: B, mut f: F) -> B
    where
        Self: Sized,
        F: FnMut(&mut B, Self::Item),
    {
        self.for_each(|item| f(&mut init, item));
        init
    }

@jdupak
Copy link

jdupak commented May 15, 2025

@rustbot claim

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-lint Area: New lints good first issue These issues are a good way to get started with Clippy L-perf Lint: Belongs in the perf lint group
Projects
None yet
Development

No branches or pull requests

5 participants