I have two HashMap<&str, String>
with the same keys and I wish to create one HashMap
with same keys where the values are combined. I do not want to keep references to the first two HashMap
s, but want to move the String
s to the new HashMap
.
use std::collections::HashMap;
#[derive(Debug)]
struct Contact {
phone: String,
address: String,
}
fn main() {
let mut phones: HashMap<&str, String> = HashMap::new();
phones.insert("Daniel", "798-1364".into());
phones.insert("Ashley", "645-7689".into());
phones.insert("Katie", "435-8291".into());
phones.insert("Robert", "956-1745".into());
let mut addresses: HashMap<&str, String> = HashMap::new();
addresses.insert("Daniel", "12 A Street".into());
addresses.insert("Ashley", "12 B Street".into());
addresses.insert("Katie", "12 C Street".into());
addresses.insert("Robert", "12 D Street".into());
let contacts: HashMap<&str, Contact> = phones.keys().fold(HashMap::new(), |mut acc, value| {
acc.entry(value).or_insert(Contact {
phone: *phones.get(value).unwrap(),
address: *addresses.get(value).unwrap(),
});
acc
});
println!("{:?}", contacts);
}
But I have an error
error[E0507]: cannot move out of a shared reference
--> src/main.rs:24:20
|
24 | phone: *phones.get(value).unwrap(),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ move occurs because value has type `std::string::String`, which does not implement the `Copy` trait
error[E0507]: cannot move out of a shared reference
--> src/main.rs:25:22
|
25 | address: *addresses.get(value).unwrap(),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ move occurs because value has type `std::string::String`, which does not implement the `Copy` trait
Playground
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With