generics - Merge two HashMaps in Rust -
so i'm bit stuck, trying merge 2 hashmaps.
it's easy inline:
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(); (key, value) in first_context.iter() { new_context.insert(*key, *value); } (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); }
it's easy enough make function:
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(); (key, value) in first_context.iter() { new_context.insert(*key, *value); } (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); }
however, can't seem generic version work:
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(); (key, value) in first_context.iter() { new_context.insert(*key, *value); } (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.
compiling it:
error: trait `core::kinds::sized` not implemented type `str`
i compiler confused size of generic value, i'm not sure why "str" doesn't have strict memory size? know string slice , not type, still should work, no? bug?
i thought relatively trivial function. if has solution, i'd love learn. ideally, i'd love see solution trait mergeable
, write decorator hashmap<&k, &v>, such can call let new_context = first_context.merge(&second_context);
can different question.
this version 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(); (key, value) in first_context.iter() { new_context.insert(*key, *value); } (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 in signature of merge()
. here 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 mine:
fn merge<k: hash + eq + copy, v: copy>(first_context: &hashmap<k, v>, second_context: &hashmap<k, v>) -> hashmap<k, v>
for reason trying abstract hashmap<&str, &str>
hashmap<&k, &v>
, not correct: while &str
is borrowed pointer, special - points dynamically sized type str
. size of str
not known compiler, can use through pointer. consequently, neither hash
nor eq
implemented str
, implemented &str
instead. hence i've changed hashmap<&'a k, &'a v>
hashmap<k, v>
.
the second problem in general can't write function if takes references maps. non-generic merge function works because &str
reference , references implicitly copyable. in general case, however, both keys , values can non-copyable, , merging them single map require moving these maps function. adding copy
bound allows that.
you can add clone
bound instead of copy
, 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> { // ... (key, value) in first_context.iter() { new_context.insert(key.clone(), value.clone()); } // ... }
the general way, however, moving maps function:
fn merge<k: hash + eq, v>(first_context: hashmap<k, v>, second_context: hashmap<k, v>) -> hashmap<k, v> { // ... (key, value) in first_context.into_iter() { new_context.insert(key, value); } // ... }
note into_iter()
method consumes map, returns iterator of tuples actual values instead of references.
Comments
Post a Comment