rust 为什么String类型的deref()操作返回str的引用,而Asterisk操作返回str?

jm81lzqq  于 2023-01-30  发布在  其他
关注(0)|答案(2)|浏览(151)

我是Rust的新手,我发现*操作符可以被Deref trait重载。std::string::String类型实现了Deref trait,返回&str类型。但是当我做下面的测试时,编译器告诉我s2的类型是str,出现错误消息“str类型的值的大小在编译时无法得知”。因此代码无法编译。但问题是为什么s2str?它不应该与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
6qftjkof

6qftjkof1#

Deref是Rust中的一个特殊特性,可以在docs中找到规则。还有一些其他地方会发生Deref强制,但既然您询问了一元*,那么该页上的第一条规则与您相关。
如果T实现Deref<Target = U>,并且xT类型的值,则:

  • 在不可变上下文中,*x(其中T既不是引用也不是原始指针)等价于*Deref::deref(&x)

所以在Deref::deref被调用后,Rust再次尝试一元*。这 * 可以 * 在其他类型上调用Deref,如this question中所示。这也是C++的可重载->操作符的工作方式。它执行某种(用户定义的)强制,然后再次尝试取消引用,这可能会在其他对象上递归调用->
所以这个

let s2 = *owned;

相当于

let s2 = *owned.deref();

并且具有str类型。str不是Sized类型,因此不能存储在变量中,这会导致错误。
至于 * 为什么 * Rust要这么做,Deref trait被定义为接受一个引用并返回一个引用。这是有意义的,因为它在幕后强制某种引用,而不是实际创建数据。十有八九,Deref只是返回一个对外部结构上的一些内部数据的引用(Box是一个很好的例子)。
另一方面,当你作为程序员编写*时,你显然不需要引用,毕竟你只是用自己的方式对数据进行了"解引用",所以*允许通过Deref trait进行deref强制,但在强制完成后,仍然尝试获取(或复制,如果适用)数据的所有权。

np8igboo

np8igboo2#

让我们再看看Deref和解引用操作符之间的关系:

let owned = "test".to_string(); // owned type is String
let s1 = owned.deref();  // type of s1 is &str
let s2 = &*owned;   // type of s2 is also &str
//let s3 = *owned;  // if this compiled, type of s3 would be str

注意*x如何扩展到*x.deref(),而不是扩展到x.deref()本身。这可以看作是deref()在应用实际的解引用操作符之前的“预处理”步骤。这就是为什么上面的示例需要&*owned,以及为什么*owned不能编译,尽管owned.deref()编译得很好。

相关问题