I have different structs which all contain a HashMap
with String
as the key, but with different value types. For example, one struct has a member of type HashMap<String, String>
, the other will have a member of type HashMap<String, u8>
, and so on.
I want to define a method that can access these HashMap
members and do generic actions on them that don't involve the value. For example, I want to count the number of keys, remove a key, check if a key exists, etc. I'm not sure how to implement this behavior.
The best way I had in mind so far is to define a trait that has a method that exposes the HashMap
and let each struct implement it. However I don't know how to write this trait and method in a way that "ignores" the value type. I tried using a wildcard (_
) but it doesn't work. How do I implement this?
Here is my code (which doesn't compile):
use std::collections::HashMap;
pub trait HashMapContainer {
fn get_hash_map(&self) -> HashMap<String, _>;
}
struct HashMapContainerImpl1 {
map: HashMap<String, String>,
}
impl HashMapContainerImpl1 {
pub fn new() -> HashMapContainerImpl1 {
HashMapContainerImpl1 {
map: HashMap::new(),
}
}
fn internal_logic_on_map(&mut self) {
//....
}
}
impl HashMapContainer for HashMapContainerImpl1 {
fn get_hash_map(&self) -> HashMap<String, _> {
self.map
}
}
struct HashMapContainerImpl2 {
map: HashMap<String, u8>,
}
impl HashMapContainerImpl2 {
pub fn new() -> HashMapContainerImpl2 {
HashMapContainerImpl2 {
map: HashMap::new(),
}
}
fn internal_logic_on_map(&mut self) {
//....
}
}
impl HashMapContainer for HashMapContainerImpl2 {
fn get_hash_map(&self) -> HashMap<String, _> {
self.map
}
}
fn do_generic_actions_on_map(hm_container: &HashMapContainer) {
println!("key count: {}", hm_container.get_hash_map().len());
println!(
"key esists? {}",
hm_container.get_hash_map().get("key1").is_some()
);
hm_container.get_hash_map().remove("key2");
}
fn main() {
let cont1 = HashMapContainerImpl1::new();
let cont2 = HashMapContainerImpl2::new();
do_generic_actions_on_map(cont1);
do_generic_actions_on_map(cont2);
}
With an associated type
The code below using a generic is correct, but after thinking about it, I think that using an associated type might be more suitable here. The trait should look like this:
The difference is, that you now can only implement the trait once for a struct and not multiple times, which is more correct in this case.
The implementations are roughly the same as with the generic type parameter.
(Playground)
You can also look at When is it appropriate to use an associated type versus a generic type? which explains the difference between those two pretty good.
With a generic type
This is solvable by introducing a generic type in your
HashMapContainer
trait.I changed the signature to return a reference to the
HashMap
. It would be possible to do it without, e.g. by usingclone
or takingself
as value, instead of as reference. I also introduced a_mut
version.The implementation is straight foreword: