How can I create a HashMap with values moved from two other HashMaps?
HashMap::get
returns an Option<&V>
, that is, a reference to the value inside the map. You cannot move out of a reference with *
unless V
implements Copy
. You need a different method that moves the value out of the map, which is HashMap::remove
(note that it returns Option<V>
).
If you try to rewrite the same algorithm using remove
, you'll get a different error:
let contacts: HashMap<&str, Contact> = phones.keys().fold(HashMap::new(), |mut acc, value| {
acc.entry(value).or_insert(Contact {
phone: phones.remove(value).unwrap(),
address: addresses.remove(value).unwrap(),
});
acc
});
error[E0502]: cannot borrow `phones` as mutable because it is also borrowed as immutable
--> src/main.rs:22:79
|
22 | let contacts: HashMap<&str, Contact> = phones.keys().fold(HashMap::new(), |mut acc, value| {
| ------ ---- ^^^^^^^^^^^^^^^^ mutable borrow occurs here
| | |
| | immutable borrow later used by call
| immutable borrow occurs here
23 | acc.entry(value).or_insert(Contact {
24 | phone: phones.remove(value).unwrap(),
| ------ second borrow occurs due to use of `phones` in closure
error: aborting due to previous error
For more information about this error, try `rustc --explain E0502`.
This error is telling you that you can't mutate a data structure while iterating over it, because mutating the data structure may invalidate the iterator. Sometimes you can solve this with interior mutability, but in this case you don't need to do anything like that. Just call phones.into_iter()
to move the phone numbers out of the map while you iterate. Then it's easy to use a map
to create (&str, Contact)
tuples and, finally, collect
it all back into a HashMap
.
let contacts: HashMap<_, _> = phones
.into_iter()
.map(|(key, phone)| {
(
key,
Contact {
phone,
address: addresses.remove(key).unwrap(),
},
)
})
.collect();
Playground