rust-clippy
rust-clippy copied to clipboard
Clippy `contains_key` followed by `insert` suggestion won’t compile
Summary
From https://users.rust-lang.org/t/clippy-contains-key-followed-by-insert-suggestion-wont-compile/100243
Clippy's suggestion of replacement code doesn't compile
See Playground 1
Reproducer
I tried this code:
use std::collections::BTreeMap;
#[derive(PartialEq, PartialOrd, Eq, Ord, Clone, Copy, Debug)]
struct Pos(usize, usize);
fn can_get_to(start: Pos, end: Pos) -> Option<u32> {
if start == Pos(1,1) && end == Pos(2,2) {
Some(5)
} else {
None
}
}
fn main() {
let mut distance: BTreeMap<Pos, u32> = BTreeMap::new();
let posnear = Pos(1,1);
let posfar = Pos(2,2);
distance.insert(posnear, 10);
if !distance.contains_key(&posfar) {
if let Some(dist) = can_get_to(posnear, posfar) {
distance.insert(posfar, distance[&posnear] + dist);
}
}
// Clippy's suggestion for above - won't compile
// if let std::collections::btree_map::Entry::Vacant(e) = distance.entry(posfar) {
// if let Some(dist) = can_get_to(posnear, posfar) {
// e.insert(distance[&posnear] + dist);
// }
// }
dbg!(distance);
}
And the compile error from Clippy's suggestion:
error[E0502]: cannot borrow `distance` as immutable because it is also borrowed as mutable
--> src/main.rs:31:22
|
29 | if let std::collections::btree_map::Entry::Vacant(e) = distance.entry(posfar) {
| ---------------------- mutable borrow occurs here
30 | if let Some(dist) = can_get_to(posnear, posfar) {
31 | e.insert(distance[&posnear] + dist);
| ------ ^^^^^^^^ immutable borrow occurs here
| |
| mutable borrow later used by call
Version
No response
Additional Labels
@rustbot label +I-suggestion-causes-error
A somewhat easy fix for this specific case could be to check if the receiver of the .insert() call is a path to a local, then take that local ID and check if there is a path expression to that local anywhere in the insert argument, and if so, maybe just don't lint at all?
But that approach won't cover all cases and fixing this generally probably requires looking at MIR/lifetimes, because the map can be accessed through borrowers
let mut map: BTreeMap<Pos, u32> = BTreeMap::new();
let map_ref = ↦
if !map.contains_key(&posfar) {
map.insert(posfar, map_ref[&posnear] + dist);
}
Here map and map_ref refer to the same map (and will fail to compile with the suggestion applied), but they are still different locals, so that fix wouldn't catch it.
@rustbot claim