我正在尝试实现与HashMap::entry
类似的功能,但不允许使用密钥(请参阅具有类似用途的this RFC)。以下是我的代码,模仿Index
impl
后的HashMap
(见this和this)。
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::Owned
是K
。如果确实如此,我们该如何处理?
答案 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来解决实施中的其余问题。