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

Tags:

Hashmap

Rust