Merge two HashMaps in Rust

后端 未结 2 574
无人共我
无人共我 2021-01-02 00:30

So I\'m a bit stuck, trying to merge two HashMaps.

It\'s easy to do it inline:

fn inline() {
    let mut first_context = HashMap::new();
    first_co         


        
相关标签:
2条回答
  • 2021-01-02 00:57

    A more up to date answer from 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

    0 讨论(0)
  • 2021-01-02 01:12

    This version does work:

    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);
    }
    

    The difference is in the signature of merge(). Here is yours:

    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>
    

    Here is mine:

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

    For some reason you are trying to abstract HashMap<&str, &str> to HashMap<&K, &V>, but this is not really correct: while &str is a borrowed pointer, it is special - it points to dynamically sized type str. Size of str is not known to the compiler, so you can use it only through a pointer. Consequently, neither Hash nor Eq are implemented for str, they are implemented for &str instead. Hence I've changed HashMap<&'a K, &'a V> to HashMap<K, V>.

    The second problem is that in general you can't write your function if it takes only references to maps. Your non-generic merge function works only because &str is a reference and references are implicitly copyable. In general case, however, both keys and values can be non-copyable, and merging them into the single map will require moving these maps into the function. Adding Copy bound allows that.

    You can also add Clone bound instead of Copy and use explicit clone() call:

    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());
        }
        // ...
    }
    

    The most general way, however, is moving maps into the function:

    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);
        }
        // ...
    }
    

    Note into_iter() method which consumes the map, but returns an iterator of tuples with actual values instead of references.

    0 讨论(0)
提交回复
热议问题