我想编写一个具有内存大小限制的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
作为底层结构,密钥与值一起存储,我希望哈希能够找到它。我将工作哈希键作为对真实密钥的引用,并提供Hash
和PartialEq
函数,以便HashMap
可以找到并使用密钥进行分组。这很简单。
我明白我必须比较这两个PartialEq
,所以我必须使用*self.k
取消引用当前对象,所以为什么&*other.k
为另一个对象?这就是我不明白的地方。为什么不只是*other.k
?我不是只是解除引用两者所以我可以比较实际的密钥吗?
我们想打电话给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
,从而实现签名。
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
是被调用者。