如何编写函数来处理通用映射及其条目类型?

时间:2018-06-07 21:19:22

标签: dictionary generics rust

我需要保护HashMap覆盖现有值的函数,所以我写了一个简单的函数:

fn insert_or_panic<K, V>(m: &mut HashMap<K, V>, k: K, v: V)
where
    K: Hash + Eq,
{
    use std::collections::hash_map::Entry;
    match m.entry(k) {
        Entry::Vacant(o) => o.insert(v),
        Entry::Occupied(_) => panic!("attempt to overwrite entry in dictionary"),
    };
}

现在我想概括它以使用任何类型的地图:

fn generic_insert_or_panic<M, K, V>(m: &mut M, k: K, v: V)
where
    K: Hash + Eq,
    M: Map<K, V>,
{
    use std::collections::hash_map::Entry;
    match m.entry(k) {
        Entry::Vacant(o) => o.insert(v),
        Entry::Occupied(_) => panic!("attempt to overwrite entry in dictionary"),
    };
}

我感兴趣的HashMap所有方法都在impl块中与任何特征无关,所以看起来我必须实现一个新的特性:

trait Map<K: Hash + Eq, V> {
    fn entry(&mut self, key: K) -> Entry<K, V>;
}

每张地图都有自己的Entry类型:std::collections::hash_map::Entrystd::collections::btree_map::Entry,而且我没有看到替换Entry的合适特征。是时候换个性格吗?

我觉得我的方向错了。如何解决这个问题?

我刚开始学习Rust,我的一些问题可能看起来不切实际和牵强附会,但我正在寻找解决问题的方法,而不是解决问题,或至少知道限制语言。

常见问题解答explains高等级的类型可以解决这个问题,但这是一个很大的特点,因此生锈需要仔细处理。

2 个答案:

答案 0 :(得分:0)

您可以创建此特征

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

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

然后只需编写通用insert_or_panic即可使用contains_keyinsert而不是Entry API。

但是,只有两种类型的地图,它们的键需要不同的东西,特别是BTreeMap需要有序的键。这听起来像写一个通用函数而不是两个函数的情况是过度的。

答案 1 :(得分:0)

  

是时候进入另一个特质吗?

是。没有人需要这个特殊的通用表达式,所以你可以自己抽象出来。

trait InsertOrPanic<K, V> {
    fn generic_insert_or_panic(&mut self, k: K, v: V);
}

use std::collections::HashMap;
use std::hash::Hash;

impl<K, V> InsertOrPanic<K, V> for HashMap<K, V>
where
    K: Eq + Hash,
{
    fn generic_insert_or_panic(&mut self, k: K, v: V) {
        use std::collections::hash_map::Entry;

        match self.entry(k) {
            Entry::Vacant(o) => o.insert(v),
            Entry::Occupied(_) => panic!("attempt to overwrite entry in dictionary"),
        };
    }
}

use std::collections::BTreeMap;

impl<K, V> InsertOrPanic<K, V> for BTreeMap<K, V>
where
    K: Ord,
{
    fn generic_insert_or_panic(&mut self, k: K, v: V) {
        use std::collections::btree_map::Entry;

        match self.entry(k) {
            Entry::Vacant(o) => o.insert(v),
            Entry::Occupied(_) => panic!("attempt to overwrite entry in dictionary"),
        };
    }
}

请注意,每个实现的边界是不同的(Hash + EqOrd),因此它们的根本不同。

  

我觉得我的方向错了。如何解决这个问题?

     

我刚刚开始学习Rust,我的一些问题可能看起来不切实际和牵强附会,但我正在寻找解决问题的方法

您尚未提供足够的信息来证明您的需求,因此我要对此声明提出质疑。通常,您不需要在两种类型的地图上使用通用,因为问题域自然会倾向于其中一种。