自动解引用和解引用强制转换之间的关系是什么?

20

在一些讨论之后,我现在对于自动解引用(auto-dereferencing)解引用强制转换(deref coercion)之间的关系有些困惑。

看起来,术语"自动解引用"仅适用于要解引用的目标是一个方法接收器的情况, 而似乎术语"解引用强制转换"适用于函数参数以及所有需要的上下文。

我认为解引用并不总是涉及到解引用强制转换,但我不确定:解引用是否总是使用一些Deref :: deref特质实现?

如果是这样,那么T: Deref<Target = U> where T: &U的实现者是否内置于编译器中?

最后,在编译器隐式地将&&&&x转换为&x的所有情况下,使用术语"自动解引用"听起来很自然:

pub fn foo(_v: &str) -> bool {
    false
}

let x="hello world";
foo(&&&&x);

这是社区的普遍共识吗?
1个回答

24
这两种情况之间的相似之处相当肤浅。
在方法调用表达式中,编译器首先需要确定要调用哪个方法。该决策基于接收者的类型。编译器构建一个候选接收者类型列表,其中包括通过重复取消引用接收者而获得的所有类型,但也包括对于遇到的所有类型T的&T和&mut T。这就是为什么您可以直接调用接收&mut self的方法作为x.foo()而不必写(&mut x).foo()的原因。对于候选列表中的每种类型,编译器然后查找固有方法和可见特征上的方法。有关详细信息,请参见语言参考
取消引用强制转换则非常不同。它仅在编译器确切知道期望的类型的强制转换站点发生。如果实际遇到的类型与期望的类型不同,则编译器可以使用任何强制转换,包括取消引用强制转换,将实际类型转换为期望的类型。可能的强制转换列表包括不定大小的强制转换、指针弱化和取消引用强制转换。有关详细信息,请参见Nomicon中的强制转换章节
所以,这实际上是两种完全不同的机制——一种用于找到正确的方法,另一种用于在已知期望类型的情况下进行类型转换。第一个机制还自动引用接收器,这在强制转换中永远不会发生。
并非每次解引用都是解引用强制转换。如果写*x,则明确地对x进行了解引用。相反,只有编译器在已知期望类型的位置才会隐式执行解引用强制转换x类型是否为指针类型(即引用或原始指针)取决于解引用的语义,对于指针类型,*x表示x指向的对象,而对于其他类型,*x等效于*Deref::deref(&x)(或其可变版本)。
如果是这样,那么T: Deref<Target = U> where T: &U的实现者是否内置于编译器中?
我不太确定你的语法应该意味着什么 - 它肯定不是有效的 Rust 语法 - 但我猜你是在问是否将 &T 的实例解引用为 T 是内置于编译器中的。如上所述,指针类型(包括引用)的解引用已内置于编译器中,但标准库中也有一个 &T 的通用 Deref 实现。这个通用实现对于泛型代码非常有用 - 否则,特质约束 T: Deref<Target = U> 将不允许 T = &U

我认为重点在最后一句话。重新表述我的问题:编译器所知道的解引用语义可以称为自动解引用吗?在强制转换站点,不仅有“解引用强制转换”机制,因为正如你指出的那样,延迟毯子impl fn deref(&self) -> &T { *self }是幂等的,并且它不能解释 &&&&x -> &x。 - attdona
@attdona 我跟不上。自动解引用意味着隐式而非显式解引用。这与解引用的语义无关。我也不能理解在这个上下文中“幂等”的含义。只有当范围是域的子集时,函数才能是幂等的,而这在这里不是情况——该函数将类型为 &&T 的值映射到类型为 &T 的值。 - Sven Marnach
1
@attdona 对不起,我还是无法理解。毯子取消引用实现的原型本质上是 deref(&&T) -> &T,而您似乎认为它是 deref(&T) -> &T。不过我可能误读了您的评论。 - Sven Marnach
有人能解释一下为什么对于&T的全局实现中,Deref将&&T映射到&T吗? fn deref(&self) -> &T,&T是从deref函数返回的。Deref被实现为&T,并且返回值是&T,那么它如何将&&T映射到&T呢?非常感谢。 - Joey
1
@Joey self 参数的类型为 &Self,而 Self&T,因此 &Self 就是 &&Tderef() 函数的返回类型显然是 &T,因此该函数从 &&T 映射到 &T - Sven Marnach
显示剩余3条评论

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