Skip to content
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

Clippy suggests: more directly with map_or_else(g, f) instead... recommendation does not compile #4144

Closed
EdmundsEcho opened this issue May 25, 2019 · 8 comments · Fixed by #4164
Labels
C-bug Category: Clippy is not doing the correct thing

Comments

@EdmundsEcho
Copy link

EdmundsEcho commented May 25, 2019

Great tool! Thank you.

The code where clippy suggests I use a more direct approach:

        frequencies
            .get_mut(word)
            .map(|count| {
                *count += 1;
            })
            .unwrap_or_else(|| {
                frequencies.insert(word.to_owned(), 1);
            });

My interpretation of the suggestion:

        frequencies.get_mut(word).map_or_else(
            || {
                frequencies.insert(word.to_owned(), 1); 
                ^^^^^^^^^^^^  <<<< cannot borrow frequencies as mutable more than once at a time
            },
            |count| {
                *count += 1;
            },
        )

The caveat of course is that there is an easy fix to the error I'm receiving.

Notwithstanding, my first guess is that there is an inconsistency here:

  • either the recommendation is right most of the time except in the situation I have here,
  • or the map_or_else should work the same way as my first iteration of the code all the time.

I hope this is helpful.

- E

@flip1995
Copy link
Member

flip1995 commented May 28, 2019

This looks like you want to do this:

let count = frequencies.entry(word).or_insert(0);
*count += 1;

https://doc.rust-lang.org/std/collections/hash_map/struct.HashMap.html#method.entry

@flip1995 flip1995 added the C-bug Category: Clippy is not doing the correct thing label May 28, 2019
@EdmundsEcho
Copy link
Author

Thank you for the pointer. I compiled the code to confirm that unfortunately the suggestion does not solve the issue. The motivation of the code as I have it, is to avoid instantiating a String before determining if I can get away with simply incrementing the count of the key already in the hash. The suggested code requires that word be a String, and thus instantiating accordingly, without regard of the existence of the key.

@flip1995
Copy link
Member

You could try to do this with contains_key:

if freq.contains_key(word) {
    let count = freq.get_mut(word).unwrap();
    *count += 1;
} else {
    freq.insert(word.to_string(), 1);
}

@EdmundsEcho
Copy link
Author

Thank you. This was useful. At this point only as a potentially useful FYI, the latest code executes consistently about 5-10% slower than the ugly syntax I was using. This experience has likely fallen into the scope of the methods provided by HashMap.

@flip1995
Copy link
Member

Yeah iterators can be optimized more efficiently, than if-else-blocks. Have you tested the performance of the entry method (even with the allocation, I could imagine, that this is even faster)?

@EdmundsEcho
Copy link
Author

Yes, I tried the code using entry. The performance was similar to the contains_key option; about 5-10% slower... I believe I saw somewhere a RFC suggesting an update or new function for HashMap to address this potentially missed opportunity.

@ghost
Copy link

ghost commented Jun 2, 2019

What about this?

    match frequencies.get_mut(word) {
        Some(count) => { *count += 1; }
        None => { frequencies.insert(word.to_owned(), 1); }
    }

It should perform as the same as map_or_else as it's just an "inlining" of that method ( https://doc.rust-lang.org/src/core/option.rs.html#456-461 ).

IMO, it's better style to use match statements than side-effects in map calls.

@EdmundsEcho
Copy link
Author

@mikerite - Your latest code performs as quickly as the original code I performed. The syntax is much nicer. Thank you!

FYI: I agree with you re-style of if/then versus match. clippy however suggested I use if/then for this use of match. I'm not sure clippy should opine one way or the other in this case.

bors added a commit that referenced this issue Jun 4, 2019
Fix .map(..).unwrap_or_else(..) bad suggestion

Closes #4144
@bors bors closed this as completed in #4164 Jun 4, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-bug Category: Clippy is not doing the correct thing
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants