Skip to content

clippy suggests map.entry().or_insert() when it shouldn't #1450

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

Closed
tspiteri opened this issue Jan 18, 2017 · 2 comments · Fixed by #6937
Closed

clippy suggests map.entry().or_insert() when it shouldn't #1450

tspiteri opened this issue Jan 18, 2017 · 2 comments · Fixed by #6937
Labels
C-bug Category: Clippy is not doing the correct thing E-medium Call for participation: Medium difficulty level problem and requires some initial experience. I-false-positive Issue: The lint was triggered on code it shouldn't have

Comments

@tspiteri
Copy link
Contributor

use std::collections::HashMap;
fn expensive_can_fail(v: u32) -> Result<u32, ()> {
    if v == 0 { Err(()) } else { Ok(v) }
}
fn check(map: &mut HashMap<u32, u32>, v: u32) -> Result<(), ()> {
    if !map.contains_key(&v) {
        map.insert(v, expensive_can_fail(v)?);
    }
    Ok(())
}
fn main() {
    let mut map = HashMap::new();
    let _ = check(&mut map, 0);
}

In the above, clippy suggests map.entry(v).or_insert(expensive_can_fail(v)?), but that calls the expensive function even if it is not needed.

Normally you could use map.entry(v).or_insert_with(/*...*/) to call the function only when needed, but in this case, since expensive_can_fail(v)? can return, that doesn't work either. Maybe in this case clippy can suggest

    if let Entry::Vacant(o) = map.entry(v) {
        o.insert(expensive_can_fail(v)?);
    }

which avoids calling the expensive function needlessly and avoids the double key search.

@oli-obk oli-obk added E-medium Call for participation: Medium difficulty level problem and requires some initial experience. C-bug Category: Clippy is not doing the correct thing labels Jan 19, 2017
@MichaelMauderer
Copy link

There is another case when this warning crops up when it should not: when the function that is called to produce the value that is inserted requires a borrow of an object owning the map.

For example:

fn fill_value(&mut self, key: u32) {
        if !self.map.contains_key(&key) {
            self.map
                .insert(key, self.make_something_expensive_with_self(key));
        }
    }

As far as I can see, this can't be rewritten with the entry API as the entry will keep a mutable reference to self, prohibiting the second borrow to self required for the function cal to make_something_expensive_with_self.

@kayleg
Copy link

kayleg commented Apr 25, 2019

I think there is a false positive with 2 different maps triggering this too:

fn map_issue() {
    let mut m1: HashMap<u32, bool> = HashMap::new();
    let mut m2 = HashMap::new();
    m2.insert(1, true);

    if m1.contains_key(&1) {
        m2.insert(1, false);
        m1.remove(&1);
    } else {
        let might_be_falsy = m2.remove(&1).unwrap();
        if !might_be_falsy {
            m2.insert(1, true);
            println!("It was falsy");
        } else {
            m2.insert(1, might_be_falsy);
        }
    }
}

Results in: consider using: m1.entry(1)

If you comment out the m2.insert lines in the contains_key else block it goes away, but the contains check is on m1.

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 E-medium Call for participation: Medium difficulty level problem and requires some initial experience. I-false-positive Issue: The lint was triggered on code it shouldn't have
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants