下面两个例子等价吗?
示例 1:
let x = String::new();
let y = &x[..];
示例 2:
let x = String::new();
let y = &*x;
一个比另一个更有效还是它们基本相同?
最佳答案
在 String
的情况下和 Vec
,他们做同样的事情。然而,总的来说,它们并不完全相同。
首先你要了解 Deref
.在类型逻辑上“包装”一些较低级别的更简单值的情况下,会实现此特征。例如,所有“智能指针”类型( Box
、 Rc
、 Arc
)都实现了 Deref
让您可以访问其内容。
它也适用于 String
和 Vec
:String
“解引用”到更简单的 str
, Vec<T>
取消引用更简单的 [T]
.
写作 *s
只是手动调用 Deref::deref
转s
变成“更简单的形式”。几乎总是写成 &*s
,然而:虽然 Deref::deref
签名说它返回一个借用的指针( &Target
),编译器插入第二个自动解引用。这是这样的,例如,{ let x = Box::new(42i32); *x }
结果是 i32
而不是 &i32
.
所以&*s
真的只是 Deref::deref(&s)
的简写.s[..]
是 s.index(RangeFull)
的语法糖,由 Index
实现特征。这意味着对被索引事物的“整个范围”进行切片;对于两者 String
和 Vec
,这为您提供了整个内容的一部分。同样,结果在技术上是一个借用的指针,但 Rust 也会自动解引用这个指针,所以它也几乎总是写成 &s[..]
.
那么有什么区别呢?保持这个想法;咱们聊聊Deref
链接。
举个具体的例子,因为你可以查看一个String
作为 str
,在 str
上提供所有方法会非常有帮助。 s 在 String
上自动可用s 也是。 Rust 通过 Deref
做到这一点,而不是继承。链接。
它的工作方式是,当您要求对某个值使用特定方法时,Rust 首先查看为该特定类型定义的方法。假设它没有找到您要求的方法;在放弃之前,Rust 会检查 Deref
执行。如果它找到一个,它会调用它,然后再试一次。
这意味着当您调用 s.chars()
时哪里s
是 String
,实际发生的情况是您正在调用 s.deref().chars()
, 因为 String
没有名为 chars
的方法,但是 str
does (向上滚动可以看到 String
只得到这个方法,因为它实现了 Deref<Target=str>
)。
回到最初的问题,&*s
之间的区别和 &s[..]
在 s
时会发生什么不仅仅是String
或 Vec<T>
.让我们举几个例子:
s: String
; &*s: &str
, &s[..]: &str
. s: &String
:&*s: &String
, &s[..]: &str
. s: Box<String>
:&*s: &String
, &s[..]: &str
. s: Box<Rc<&String>>
:&*s: &Rc<&String>
, &s[..]: &str
. &*s
只剥离一层间接。 &s[..]
剥掉所有这些。这是因为 Box
都没有, Rc
, &
等实现 Index
特质,所以 Deref
链接导致调用 s.index(RangeFull)
链接所有这些中间层。你应该使用哪一个?随心所欲。使用
&*s
(或 &**s
,或 &***s
)如果您想精确控制要剥离的间接层数。使用 &s[..]
如果您想将它们全部剥离并获得值的最内层表示。或者,您可以按照我的操作使用
&*s
因为它从左到右读取,而 &s[..]
再次从左到右到左阅读,这让我很恼火。 :)附录
Deref
coercions的相关概念. DerefMut
和 IndexMut
可以完成上述所有操作,但对于 &mut
而不是 &
. 关于memory - 将字符串转换为 &strs 时,切片和显式重新借用之间有区别吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33860750/