【问题标题】:Merge two HashMaps in Rust在 Rust 中合并两个 HashMap
【发布时间】:2018-11-11 01:43:12
【问题描述】:

所以我有点卡住了,试图合并两个 HashMap。

内联很容易:

fn inline() {
    let mut first_context = HashMap::new();
    first_context.insert("Hello", "World");
    let mut second_context = HashMap::new();
    second_context.insert("Hey", "There");

    let mut new_context = HashMap::new();
    for (key, value) in first_context.iter() {
        new_context.insert(*key, *value);
    }
    for (key, value) in second_context.iter() {
        new_context.insert(*key, *value);
    }
    println!("Inline:\t\t{}", new_context);
    println!("Inline:\t\t{}\t{} [Initial Maps Still Usable]", first_context, second_context);
}

制作函数很容易:

fn abstracted() {
    fn merge<'a>(first_context: &HashMap<&'a str, &'a str>, second_context: &HashMap<&'a str, &'a str>) -> HashMap<&'a str, &'a str> {
        let mut new_context = HashMap::new();
        for (key, value) in first_context.iter() {
            new_context.insert(*key, *value);
        }
        for (key, value) in second_context.iter() {
            new_context.insert(*key, *value);
        }
        new_context
    }

    let mut first_context = HashMap::new();
    first_context.insert("Hello", "World");
    let mut second_context = HashMap::new();
    second_context.insert("Hey", "There");

    println!("Abstracted:\t{}", merge(&first_context, &second_context));
    println!("Abstracted:\t{}\t{} [Initial Maps Still Usable]", first_context, second_context);
}

但是,我似乎无法让通用版本工作:

fn generic() {
    fn merge<'a, K: Hash + Eq, V>(first_context: &HashMap<&'a K, &'a V>, second_context: &HashMap<&'a K, &'a V>) -> HashMap<&'a K, &'a V> {
        let mut new_context = HashMap::new();
        for (key, value) in first_context.iter() {
            new_context.insert(*key, *value);
        }
        for (key, value) in second_context.iter() {
            new_context.insert(*key, *value);
        }
        new_context
    }

    let mut first_context = HashMap::new();
    first_context.insert("Hello", "World");
    let mut second_context = HashMap::new();
    second_context.insert("Hey", "There");

    println!("Generic:\t{}", merge(&first_context, &second_context));
    println!("Generic:\t{}\t{} [Initial Maps Still Usable]", first_context, second_context);
}

The above code on play.rust-lang.org.

编译:

error: the trait `core::kinds::Sized` is not implemented for the type `str`

我知道编译器对泛型值的大小感到困惑,但我不确定为什么“str”没有严格的内存大小?我知道它是一个字符串切片而不是一个类型,但这仍然可以工作,不是吗?这是一个错误吗?

我认为这将是一个相对微不足道的功能。如果有人有好的解决方案,我很想学习。实际上,理想情况下,我希望看到一个带有trait Mergeable 的解决方案并为 HashMap 编写一个装饰器,这样我就可以调用let new_context = first_context.merge(&amp;second_context); 但这可能是一个不同的问题。

【问题讨论】:

    标签: rust


    【解决方案1】:

    来自this tweet 的最新回答:

    use std::collections::HashMap;
    
    // Mutating one map
    fn merge1(map1: &mut HashMap<(), ()>, map2: HashMap<(), ()>) {
        map1.extend(map2);
    }
    
    // Without mutation
    fn merge2(map1: HashMap<(), ()>, map2: HashMap<(), ()>) -> HashMap<(), ()> {
        map1.into_iter().chain(map2).collect()
    }
    
    // If you only have a reference to the map to be merged in
    fn merge_from_ref(map: &mut HashMap<(), ()>, map_ref: &HashMap<(), ()>) {
        map.extend(map_ref.into_iter().map(|(k, v)| (k.clone(), v.clone())));
    }
    

    Rust Playground Link

    【讨论】:

      【解决方案2】:

      这个版本确实有效:

      use std::collections::HashMap;
      use std::hash::Hash;
      
      fn main() {
          fn merge<K: Hash + Eq + Copy, V: Copy>(first_context: &HashMap<K, V>, second_context: &HashMap<K, V>) -> HashMap<K, V> {
              let mut new_context = HashMap::new();
              for (key, value) in first_context.iter() {
                  new_context.insert(*key, *value);
              }
              for (key, value) in second_context.iter() {
                  new_context.insert(*key, *value);
              }
              new_context
          }
      
          let mut first_context = HashMap::new();
          first_context.insert("Hello", "World");
          let mut second_context = HashMap::new();
          second_context.insert("Hey", "There");
      
          println!("Generic:\t{}", merge(&first_context, &second_context));
          println!("Generic:\t{}\t{} [Initial Maps Still Usable]", first_context, second_context);
      }
      

      区别在于merge()的签名。这是你的:

      fn merge<'a, K: Hash + Eq, V>(first_context: &HashMap<&'a K, &'a V>, second_context: &HashMap<&'a K, &'a V>) -> HashMap<&'a K, &'a V>
      

      这是我的:

      fn merge<K: Hash + Eq + Copy, V: Copy>(first_context: &HashMap<K, V>, second_context: &HashMap<K, V>) -> HashMap<K, V>
      

      由于某种原因,您试图将HashMap&lt;&amp;str, &amp;str&gt; 抽象为HashMap&lt;&amp;K, &amp;V&gt;,但这并不正确:虽然&amp;str 一个借用的指针,但它很特别——它动态指向大小类型str。编译器不知道str 的大小,因此您只能通过指针使用它。因此,HashEq 都不是为 str 实现的,而是为 &amp;str 实现的。因此我将HashMap&lt;&amp;'a K, &amp;'a V&gt; 更改为HashMap&lt;K, V&gt;

      第二个问题是,一般来说,如果函数只需要对地图的引用,你就无法编写它。您的非泛型合并函数仅因为 &amp;str 是一个引用并且引用是可隐式复制的。然而,在一般情况下,键和值都可以是不可复制的,并且将它们合并到单个映射中需要将这些映射移动到函数中。添加Copy bound 允许这样做。

      您还可以添加Clone 绑定而不是Copy 并使用显式clone() 调用:

      fn merge<K: Hash + Eq + Clone, V: Clone>(first_context: &HashMap<K, V>, second_context: &HashMap<K, V>) -> HashMap<K, V> {
          // ...
          for (key, value) in first_context.iter() {
              new_context.insert(key.clone(), value.clone());
          }
          // ...
      }
      

      然而,最通用的方法是将地图移动到函数中:

      fn merge<K: Hash + Eq, V>(first_context: HashMap<K, V>, second_context: HashMap<K, V>) -> HashMap<K, V>  {
          // ...
          for (key, value) in first_context.into_iter() {
              new_context.insert(key, value);
          }
          // ...
      }
      

      注意into_iter() 方法使用地图,但返回具有实际值而不是引用的元组迭代器。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-05-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多