我是Rust的新手,我发现*
操作符可以被Deref
trait重载。std::string::String
类型实现了Deref
trait,返回&str
类型。但是当我做下面的测试时,编译器告诉我s2
的类型是str
,出现错误消息“str
类型的值的大小在编译时无法得知”。因此代码无法编译。但问题是为什么s2
是str
?它不应该与s1
类型相同吗?
let owned = "test".to_string(); // owned type is String
let s1 = owned.deref(); // s1 type is &str
let s2 = *owned; // s2 type is str
2条答案
按热度按时间6qftjkof1#
Deref
是Rust中的一个特殊特性,可以在docs中找到规则。还有一些其他地方会发生Deref
强制,但既然您询问了一元*
,那么该页上的第一条规则与您相关。如果
T
实现Deref<Target = U>
,并且x
是T
类型的值,则:*x
(其中T
既不是引用也不是原始指针)等价于*Deref::deref(&x)
。所以在
Deref::deref
被调用后,Rust再次尝试一元*
。这 * 可以 * 在其他类型上调用Deref
,如this question中所示。这也是C++的可重载->
操作符的工作方式。它执行某种(用户定义的)强制,然后再次尝试取消引用,这可能会在其他对象上递归调用->
。所以这个
相当于
并且具有
str
类型。str
不是Sized
类型,因此不能存储在变量中,这会导致错误。至于 * 为什么 * Rust要这么做,
Deref
trait被定义为接受一个引用并返回一个引用。这是有意义的,因为它在幕后强制某种引用,而不是实际创建数据。十有八九,Deref
只是返回一个对外部结构上的一些内部数据的引用(Box
是一个很好的例子)。另一方面,当你作为程序员编写
*
时,你显然不需要引用,毕竟你只是用自己的方式对数据进行了"解引用",所以*
允许通过Deref
trait进行deref强制,但在强制完成后,仍然尝试获取(或复制,如果适用)数据的所有权。np8igboo2#
让我们再看看
Deref
和解引用操作符之间的关系:注意
*x
如何扩展到*x.deref()
,而不是扩展到x.deref()
本身。这可以看作是deref()
在应用实际的解引用操作符之前的“预处理”步骤。这就是为什么上面的示例需要&*owned
,以及为什么*owned
不能编译,尽管owned.deref()
编译得很好。