使用 Rust 中的解引用运算符 &* 和 * 与 Self 有什么区别?

4

我希望编写一个LRU缓存,其内存大小有限制,而不是std中的“对象数量”限制。在自己试图理解之后,我作弊看了现有实现,我几乎理解了它,但这阻止了我:

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 作为基本结构,键存储在值中,并且我希望哈希函数能够找到它。我将工作哈希键设置为实际键的引用,并提供 HashPartialEq 函数,以便 HashMap 可以找到和使用键进行桶分配。这很容易。
那么我明白我必须比较两者的 PartialEq,所以对于当前对象,使用 *self.k 对其进行取消引用是有意义的,那么为什么对于other对象要使用 &*other.k?这就是我不明白的地方。为什么不只是 *other.k?难道我不是对两个对象进行取消引用以便比较实际键吗?

&在这里是因为PartialEq#eq需要一个引用; *other.k会移动键,而other.k则会传递指针。对此还有什么不清楚的吗?一些附注:你在哪里找到了那个实现?那种使用指针的方式并不是你随处可见的。 - E net4
好的,那么为什么*self.k可以工作呢?我不明白这里的不对称之处。在C++中,它不会是不对称的。我从一个旧的libcore提案中找到了这个例子:https://github.com/thestinger/rust-core/blob/master/core/lru.rs - Elf Sternberg
2个回答

7
我们希望调用 PartialEq::eq:
trait PartialEq<Rhs = Self>
where
    Rhs: ?Sized,
{
    fn eq(&self, other: &Rhs) -> bool;
}

假设默认实现中 Rhs = SelfSelf = K,我们需要两个类型为 &K&K 类型
  1. other.k 类型为 *const K
  2. *other.k 类型为 K
  3. &*other.k 类型为 &K

这些应该很容易理解。

  1. self.k 类型为 *const K
  2. *self.k 类型为 K

缺失的部分是 方法调用可以自动引用 它们被调用的值。这就是为什么没有在 C 或 C++ 中明确区分引用和值的语法(如 foo.bar() vs foo->bar())。

因此,K会自动引用以获取&K,从而满足签名。

啊,那就是缺失的部分。这个协议的奇怪后果真是让人困惑。在实际应用中看到它真是令人费解,但现在你已经记录下来了,我更好地理解了它。谢谢! - Elf Sternberg

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 是被调用者。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接