如何创建一个HashMap,将两个其他HashMap中的值移动到该HashMap中?

9

我有两个相同键的HashMap<&str, String>,我希望创建一个具有相同键但值组合在一起的HashMap。我不想保留对第一个和第二个HashMap的引用,而是想将String移动到新的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);
}

但是我遇到了一个错误。

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

游乐场

2个回答

10
HashMap::get 返回一个 Option<&V> 类型,即指向map内部值的引用。除非 V 实现了 Copy,否则您不能使用 * 移出引用。您需要使用另一种方法将值移出map,即HashMap::remove(请注意其返回类型是 Option<V>)。
如果您尝试使用 remove 重写相同的算法,则会获得不同的错误:
    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`.

这个错误告诉你,在迭代数据结构时不能改变它,因为改变数据结构可能会使迭代器无效。 有时可以使用内部可变性解决这个问题,但在这种情况下,你不需要做任何类似的操作。只需调用phones.into_iter()将电话号码从映射中移出,同时进行迭代。然后很容易使用map创建(&str, Contact)元组,最后将其全部collect回一个HashMap

    let contacts: HashMap<_, _> = phones
        .into_iter()
        .map(|(key, phone)| {
            (
                key,
                Contact {
                    phone,
                    address: addresses.remove(key).unwrap(),
                },
            )
        })
        .collect();

Playground(游乐场)


2
我个人喜欢使用flat_map版本 - Shepmaster
如果某些键属于“phones”但不存在于“addresses”,那么我会遇到崩溃的情况。但是我不知道如何避免这种情况。 - prehistoricpenguin
1
@prehistoricpenguin,你想用这些键做什么?试试Shepmaster的版本,使用flat_map,它会简单地跳过它们。 - trent

1

zip 在这里是你的好朋友。但是“业务逻辑”要求它只能用于排序映射。因此,如果你可以使用 BTreeMap 而不是 HashMap,那么以下代码就可以工作:

fn main() {
    let mut phones: BTreeMap<&str, String> = BTreeMap::new();
    ...

    let mut addresses: BTreeMap<&str, String> = BTreeMap::new();
    ...

    let contacts: BTreeMap<&str, Contact> = phones
        .into_iter()
        .zip(addresses.into_iter())
        .map(|((name, phone), (_, addr))| {
            (
                name,
                Contact {
                    phone: phone,
                    address: addr,
                },
            )
        })
        .collect();

    println!("{:#?}", contacts);
}

是的,但这看起来很奇怪,如果您有两个完全相同大小和键映射的情况,为什么不直接创建联系人而不创建过去的两个映射呢? - Stargateur
好的,我认为这种不变性只是面向对象编程的前提。 - edwardw

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接