对于String 和Vec,它们做同样的事情。然而,在一般情况下,它们并不完全等价。
首先,你必须了解Deref。这个特性是在一个类型在逻辑上“包装”一些较低级别、更简单的值的情况下实现的。例如,所有“智能指针”类型(Box、Rc、Arc)都实现了Deref,让您可以访问它们的内容。
它也为String 和Vec 实现:String“derefs”到更简单的str,Vec<T> derefs 到更简单的[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 trait 实现。这意味着对被索引的事物的“整个范围”进行切片;对于String 和Vec,这将为您提供整个内容的一部分。同样,结果技术上是一个借用的指针,但 Rust 自动取消引用这个以及,所以它也几乎总是写成&s[..]。
那么有什么区别呢?保持这个想法;让我们谈谈Deref 链接。
举一个具体的例子,因为您可以将String 视为str,所以让strs 上可用的所有方法自动在@ 上可用真的很有帮助987654364@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、&、etc. 都没有实现 Index 特征,所以 Deref 链接导致对 s.index(RangeFull) 的调用链接所有这些中间层。
你应该使用哪一个?随便你。如果您想准确地控制要剥离多少层间接,请使用&*s(或&**s,或&***s)。如果您想将它们全部剥离并获取值的最内部表示,请使用 &s[..]。
或者,您可以像我一样使用&*s,因为它从左到右读取,而&s[..] 再次从左到右到左读取,这让我很恼火。 :)
附录