我想编写一个具有内存大小限制的LRU缓存,而不是std中的“对象数量”限制。在试图弄清楚自己之后,我作弊并看了an existing implementation,而我几乎了解它,但这阻止了我:
struct KeyRef<K> {
k: *const K,
}
impl<K: Hash> Hash for LruKeyRef<K> {
fn hash<H: Hasher>(&self, state: &mut H) {
unsafe { (*self.k).hash(state) }
}
}
impl<K: PartialEq> PartialEq for LruKeyRef<K> {
fn eq(&self, other: &LruKeyRef<K>) -> bool {
unsafe { (*self.k).eq(&*other.k) }
}
}
这是我不理解的最后unsafe
行。我使用HashMap
作为底层结构,密钥与值一起存储,我希望hasher能够找到它。我将工作哈希键作为对真实密钥的引用,并提供Hash
和PartialEq
函数,以便HashMap
可以找到并使用密钥进行分组。这很容易。
我理解我必须比较两个PartialEq
,所以我必须使用*self.k
取消引用当前对象,所以为什么&*other.k
对于其他对象?这就是我不明白的地方。为什么不只是*other.k
?我不是只是解除引用,所以我可以比较实际的密钥吗?
答案 0 :(得分:6)
我们希望致电PartialEq::eq
:
trait PartialEq<Rhs = Self>
where
Rhs: ?Sized,
{
fn eq(&self, other: &Rhs) -> bool;
}
假设Rhs
= Self
和Self
= K
的默认实施,我们最终需要两种&K
类型
other.k
的类型为*const K
*other.k
的类型为K
&*other.k
的类型为&K
这应该是有道理的。
self.k
的类型为*const K
*self.k
的类型为K
缺少method calls are allowed to automatically reference the value他们被召唤的那件作品。这就是为什么引用和值没有明确的语法,就像在C或C ++中那样(foo.bar()
vs foo->bar()
)。
因此,K
会自动引用以获得&K
,从而完成签名。
答案 1 :(得分:2)
impl<K: PartialEq> PartialEq for LruKeyRef<K> {
fn eq(&self, other: &LruKeyRef<K>) -> bool {
unsafe { (*self.k).eq(&*other.k) }
}
}
在典型情况下,我们可以通过仅引用对象来调用&self
的方法。此外,还隐式强制对该对象的引用链。也就是说,我们可以写:
let a: &str = "I'm a static string";
assert_eq!(str.len(), 19);
assert_eq!((&&&&str).len(), 19);
但是,在你的情况下,我们从一个指针开始,必须在不安全的范围内明确地取消引用。以下是所有相关表达式的类型:
self.k : *const K
(*self.k) : K
other.k : *const K
&*other.k : &K
由于equals
对其右手成员进行了引用,因此我们必须将其作为参考。与C ++不同,您不能仅仅将左值作为引用传递而不使此引用传递显式,也不能将右值传递给const引用。但是,您可以将&
添加到文字中以获取对它的引用(foo(&5)
)。它只显示不对称,因为(在某种程度上)self.k
是调用者,other.k
是被调用者。