问题
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);
}
回答1:
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:
pub trait HashMapContainer {
type Value;
fn get_hash_map(&self) -> &HashMap<String, Self::Value>;
fn get_hash_map_mut(&mut self) -> &mut HashMap<String, Self::Value>;
}
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.
impl HashMapContainer for HashMapContainerImpl1 {
type Value = String;
fn get_hash_map(&self) -> &HashMap<String, Self::Value> {
&self.map
}
fn get_hash_map_mut(&mut self) -> &mut HashMap<String, Self::Value> {
&mut self.map
}
}
impl HashMapContainer for HashMapContainerImpl2 {
type Value = u8;
fn get_hash_map(&self) -> &HashMap<String, Self::Value> {
&self.map
}
fn get_hash_map_mut(&mut self) -> &mut HashMap<String, Self::Value> {
&mut self.map
}
}
(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.
pub trait HashMapContainer<T> {
fn get_hash_map(&self) -> &HashMap<String, T>;
fn get_hash_map_mut(&mut self) -> &mut HashMap<String, T>;
}
I changed the signature to return a reference to the HashMap. It would be possible to do it without, e.g. by using clone or taking self as value, instead of as reference. I also introduced a _mut version.
The implementation is straight foreword:
impl HashMapContainer<String> for HashMapContainerImpl1 {
fn get_hash_map(&self) -> &HashMap<String, String> {
&self.map
}
fn get_hash_map_mut(&mut self) -> &mut HashMap<String, String> {
&mut self.map
}
}
impl HashMapContainer<u8> for HashMapContainerImpl2 {
fn get_hash_map(&self) -> &HashMap<String, u8> {
&self.map
}
fn get_hash_map_mut(&mut self) -> &mut HashMap<String, u8> {
&mut self.map
}
}
来源:https://stackoverflow.com/questions/54231775/expose-a-hashmap-in-a-generic-way-that-disregards-the-hashmap-value