如何实现类似于HashMap :: entry的函数,它不会消耗密钥?

时间:2017-06-30 19:57:27

标签: generics rust traits

我正在尝试实现与HashMap::entry类似的功能,但不允许使用密钥(请参阅具有类似用途的this RFC)。以下是我的代码,模仿Index impl后的HashMap(见thisthis)。

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

trait MapExt<Q: ?Sized, V> {
    fn get_or_insert(&mut self, key: &Q, value: V) -> &V;
}

impl<'a, Q: ?Sized, K, V, S> MapExt<&'a Q, V> for HashMap<K, V, S>
where
    K: Eq + Hash + Borrow<Q>,
    Q: Eq + Hash + ToOwned,
    S: BuildHasher,
{
    fn get_or_insert(&mut self, key: &Q, value: V) -> &V {
        if !self.contains_key(key) {
            self.insert(key.to_owned(), value);
        }
        self[key]
    }
}

这给了我以下错误。

error[E0053]: method `get_or_insert` has an incompatible type for trait
  --> src/main.rs:15:42
   |
6  |         fn get_or_insert(&mut self, key: &Q, value: V) -> &V;
   |                                          -- type in trait
...
15 |         fn get_or_insert(&mut self, key: &Q, value: V) -> &V {
   |                                          ^^ expected reference, found type parameter
   |
   = note: expected type `fn(&mut std::collections::HashMap<K, V, S>, &&'a Q, V) -> &V`
              found type `fn(&mut std::collections::HashMap<K, V, S>, &Q, V) -> &V`

这里有什么问题,如何解决?

我认为会出现的另一个问题是编译器不知道Q::OwnedK。如果确实如此,我们该如何处理?

2 个答案:

答案 0 :(得分:4)

我也是一个相对初学者,但从我看到的有三个问题:

MapExt<&'a Q, V>

应该是

MapExt<Q, V>

否则您将特征的类型更改为参考。

Q: Eq + Hash + ToOwned,

应明确

Q: Eq + Hash + ToOwned<Owned=K>,

以便它知道to_owned()将返回什么类型。

self[key]

应该是

&self[key]

答案 1 :(得分:2)

您的问题可以简化为:

trait Alpha<A> {
    fn alpha(&self, key: &A);
}

impl<'a, A> Alpha<&'a A> for () {
    fn alpha(&self, key: &'a A) {}
}

基本上,您正在混淆泛型类型。使用单独的字母使其更清晰:

impl<'a, B> Alpha<&'a B> for () {}

也就是说,Alpha&#39; A的值实际上是&'a B。然后该特征说它需要参考该值。一个有效的实现是:

impl<'a, B> Alpha<&'a B> for () {
    fn alpha(&self, key: &&'a B) {}
}

一旦理解了这一点,就可以按照loganfsmyth's points来解决实施中的其余问题。