接受HashMap和BtreeMap的Rust函数

时间:2019-01-26 12:04:04

标签: rust

fn edit_map_values(
            map1: &mut HashMap<String, i128> || &mut BTreeMap<String, i128>){
    for tuple in map1.iter_mut() {
        if !map1.contains_key(&"key1") {
             *tuple.1 += 1;
        }
    }
    map1.insert(&"key2", 10);
}

如何编写一个可以接受HashMap和BtreeMap的函数,如上例所示?

3 个答案:

答案 0 :(得分:3)

可以使用特征来抽象类型,对于您的特定用例,您可以看一下这个更受限制的示例

use core::{borrow::Borrow, hash::Hash};
use std::collections::{BTreeMap, HashMap};

trait GenericMap<K, V> {
    fn contains_key<Q>(&self, k: &Q) -> bool
    where
        K: Borrow<Q>,
        Q: Hash + Eq + Ord;

    fn each_mut<F>(&mut self, cb: F)
    where
        F: FnMut((&K, &mut V));

    fn insert(&mut self, key: K, value: V) -> Option<V>;
}

impl<K, V> GenericMap<K, V> for HashMap<K, V>
where
    K: Eq + Hash,
{
    fn contains_key<Q>(&self, k: &Q) -> bool
    where
        K: Borrow<Q>,
        Q: Hash + Eq + Ord,
    {
        self.contains_key(k)
    }

    fn each_mut<F>(&mut self, mut cb: F)
    where
        F: FnMut((&K, &mut V)),
    {
        self.iter_mut().for_each(|x| cb(x))
    }

    fn insert(&mut self, key: K, value: V) -> Option<V> {
        self.insert(key, value)
    }
}

impl<K, V> GenericMap<K, V> for BTreeMap<K, V>
where
    K: Ord,
{
    fn contains_key<Q>(&self, k: &Q) -> bool
    where
        K: Borrow<Q>,
        Q: Hash + Eq + Ord,
    {
        self.contains_key(k)
    }

    fn each_mut<F>(&mut self, mut cb: F)
    where
        F: FnMut((&K, &mut V)),
    {
        self.iter_mut().for_each(|x| cb(x))
    }

    fn insert(&mut self, key: K, value: V) -> Option<V> {
        self.insert(key, value)
    }
}

fn edit_map_values<T: GenericMap<String, i128>>(map: &mut T) {
    map.each_mut(|(k, v)| {
        if k != "key1" {
            *v += 1;
        }
    });
    map.insert("key2".into(), 10);
}

fn main() {
    let mut hm: HashMap<String, i128> = [("One".into(), 1), ("Two".into(), 2)]
        .iter()
        .cloned()
        .collect();
    let mut btm: BTreeMap<String, i128> = [("Five".into(), 5), ("Six".into(), 6)]
        .iter()
        .cloned()
        .collect();
    dbg!(&hm);
    dbg!(&btm);
    edit_map_values(&mut hm);
    edit_map_values(&mut btm);
    dbg!(&hm);
    dbg!(&btm);
}

答案 1 :(得分:1)

在1.0版本之前,曾经有MapMutableMap特征,但是在稳定之前已将其删除。由于缺少更高种类的类型,Rust类型系统目前无法很好地表达这些特征。

eclectic crate提供了实验性的收集特征,但是它们尚未更新一年,因此我不确定它们是否仍适用于Rust的最新版本。

更多信息:

答案 2 :(得分:0)

虽然没有共同的 Map 特征,但您可以使用其他特征的组合对迭代器进行操作以实现类似的功能。尽管由于克隆,这可能不是非常节省内存,并且根据您尝试执行的操作类型也有点复杂。您尝试执行的操作可能是这样实现的:

fn edit_map_values<I>(map: &mut I)
where
    I: Clone + IntoIterator<Item = (String, i128)> + std::iter::FromIterator<(String, i128)>,
{
    // Since into_iter consumes self, we have to clone here.
    let (keys, _values): (Vec<String>, Vec<_>) = map.clone().into_iter().unzip();

    *map = map
        .clone()
        .into_iter()
        // iterating while mutating entries can be done with map
        .map(|mut tuple| {
            if !keys.contains(&"key1".to_string()) {
                tuple.1 += 1;
            }
            tuple
        })
        // inserting an element can be done with chain and once
        .chain(std::iter::once(("key2".into(), 10)))
        .collect();
        // removing an element could be done with filter
        // removing and altering elements could be done with filter_map
        // etc.
}

fn main() {
    use std::collections::{BTreeMap, HashMap};

    {
        let mut m = HashMap::new();
        m.insert("a".to_string(), 0);
        m.insert("key3".to_string(), 1);

        edit_map_values(&mut m);

        println!("{:#?}", m);
    }

    {
        let mut m = BTreeMap::new();
        m.insert("a".to_string(), 0);
        m.insert("key3".to_string(), 1);

        edit_map_values(&mut m);

        println!("{:#?}", m);
    }
}

两次输出是一样的,当然除了HashMap的顺序:

{
    "a": 1,
    "key2": 10,
    "key3": 2,
}