When getting a hash map Entry
, the and_modify
and or_insert
methods are alternate code paths which cannot both occur. However, the compiler seems to not factor this into its ownership logic, because it prevents a non-copyable element from being moved into the hash map from either of the possible paths:
use std::collections::HashMap;
struct NonCopyable {
name: String,
}
pub fn main() {
let mut db = HashMap::<i32, NonCopyable>::new();
let nc = NonCopyable { name: "foo".to_string() };
db.entry(0).and_modify(|value| (*value) = nc).or_insert(nc);
}
Errors:
Compiling playground v0.0.1 (/playground)
error[E0382]: use of moved value: `nc`
--> src/main.rs:9:61
|
8 | let nc = NonCopyable { name: "foo".to_string() };
| -- move occurs because `nc` has type `NonCopyable`, which does not implement the `Copy` trait
9 | db.entry(0).and_modify(|value| (*value) = nc).or_insert(nc);
| ------- -- ^^ value used here after move
| | |
| | variable moved due to use in closure
| value moved into closure here
For more information about this error, try `rustc --explain E0382`.
error: could not compile `playground` due to previous error
This can be circumvented by having the type be Clone-able and cloning it in the and_modify closure, so that it can be moved in or_insert branch. However, this seems like it should be unnecessary. In practice, my non-copyable type would be more expensive to clone than just a single string. How can I move it into the map once in either of the update or insert cases? Should I match on the Entry to handle the branching Occupied and Vacant cases?