在Rust中合并两个HashMaps

Yaz*_*d D 8 rust

所以我有点卡住,试图合并两个HashMaps.

内联很容易:

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);
}
Run Code Online (Sandbox Code Playgroud)

制作一个功能很容易:

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);
}
Run Code Online (Sandbox Code Playgroud)

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

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);
}
Run Code Online (Sandbox Code Playgroud)

上面的代码在play.rust-lang.org上.

编译它:

error: the trait `core::kinds::Sized` is not implemented for the type `str`
Run Code Online (Sandbox Code Playgroud)

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

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

Sun*_*rma 17

这条推文的最新答案:

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())));
}
Run Code Online (Sandbox Code Playgroud)

Rust Playground Link


Vla*_*eev 5

这个版本确实有效:

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);
}
Run Code Online (Sandbox Code Playgroud)

区别在于签名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>
Run Code Online (Sandbox Code Playgroud)

这是我的:

fn merge<K: Hash + Eq + Copy, V: Copy>(first_context: &HashMap<K, V>, second_context: &HashMap<K, V>) -> HashMap<K, V>
Run Code Online (Sandbox Code Playgroud)

出于某种原因,你想抽象HashMap<&str, &str>HashMap<&K, &V>,但这不是真正正确的:虽然&str 借来的指针,它是特别的-它指向的动态调整大小类型str.str编译器不知道大小,因此只能通过指针使用它.因此,既不实施Hash也不实施,而是实施它们.因此我改变了.Eqstr&strHashMap<&'a K, &'a V>HashMap<K, V>

第二个问题是,如果仅引用地图,则通常无法编写函数.您的非泛型合并函数只能&str用作引用,并且引用可以隐式复制.但是,在一般情况下,键和值都可以是不可复制的,并且将它们合并到单个映射中将需要将这些映射移动到函数中.添加Copy绑定允许.

您还可以添加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());
    }
    // ...
}
Run Code Online (Sandbox Code Playgroud)

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

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);
    }
    // ...
}
Run Code Online (Sandbox Code Playgroud)

注意into_iter()使用map的方法,但返回元组的迭代器,其中包含实际值而不是引用.