Предоставьте HashMap универсальным способом, который игнорирует значение HashMap - PullRequest
0 голосов
/ 17 января 2019

У меня есть разные структуры, которые содержат HashMap с String в качестве ключа, но с разными типами значений. Например, одна структура имеет член типа HashMap<String, String>, другая будет иметь член типа HashMap<String, u8> и т. Д.

Я хочу определить метод, который может получить доступ к этим HashMap членам и выполнять с ними общие действия, которые не связаны со значением. Например, я хочу подсчитать количество ключей, удалить ключ, проверить, существует ли ключ и т. Д. Я не уверен, как реализовать это поведение.

Лучший способ, который я имел в виду, это определить черту, которая имеет метод, который выставляет HashMap, и позволяет каждой структуре его реализовать. Однако я не знаю, как написать эту черту и метод таким образом, чтобы «игнорировать» тип значения. Я пытался использовать подстановочный знак (_), но он не работает. Как мне это реализовать?

Вот мой код (который не компилируется):

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 Ответ

0 голосов
/ 17 января 2019

со связанным типом

Код ниже с использованием универсального кода верен, но, подумав об этом, я думаю, что использование связанного типа может быть более подходящим здесь. Черта должна выглядеть так:

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

Разница в том, что теперь вы можете реализовать черту только один раз для структуры, а не несколько раз, что более правильно в этом случае.

Реализации примерно такие же, как с параметром универсального типа.

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

( Детская площадка )

Вы также можете посмотреть на Когда уместно использовать ассоциированный тип вместо универсального типа? , который объясняет разницу между этими двумя довольно хорошими.

с универсальным типом

Это можно решить, введя универсальный тип в вашу черту HashMapContainer.

pub trait HashMapContainer<T> {
    fn get_hash_map(&self) -> &HashMap<String, T>;
    fn get_hash_map_mut(&mut self) -> &mut HashMap<String, T>;
}

Я изменил подпись, чтобы вернуть ссылку на HashMap. Было бы возможно сделать это без, например, используя clone или принимая self в качестве значения, а не в качестве ссылки. Я также представил _mut версию.

Реализация является прямым предисловием:

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
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...