使用Rust解除引用运算符& * vs * with Self?

时间:2017-04-04 22:11:03

标签: rust

我想编写一个具有内存大小限制的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能够找到它。我将工作哈希键作为对真实密钥的引用,并提供HashPartialEq函数,以便HashMap可以找到并使用密钥进行分组。这很容易。

我理解我必须比较两个PartialEq,所以我必须使用*self.k取消引用当前对象,所以为什么&*other.k对于其他对象?这就是我不明白的地方。为什么不只是*other.k?我不是只是解除引用,所以我可以比较实际的密钥吗?

2 个答案:

答案 0 :(得分:6)

我们希望致电PartialEq::eq

trait PartialEq<Rhs = Self>
where
    Rhs: ?Sized,
{
    fn eq(&self, other: &Rhs) -> bool;
}

假设Rhs = SelfSelf = K的默认实施,我们最终需要两种&K类型

  1. other.k的类型为*const K
  2. *other.k的类型为K
  3. &*other.k的类型为&K
  4. 这应该是有道理的。

    1. self.k的类型为*const K
    2. *self.k的类型为K
    3. 缺少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是被调用者。