问题 将字符串转换为&strs时,切片和显式重新插入之间是否存在差异?


以下两个例子是等效的吗?

例1:

let x = String::new();
let y = &x[..];

例2:

let x = String::new();
let y = &*x;

一个比另一个更有效还是基本相同?


4927
2017-11-22 21:58


起源

如果适合答案,我也想将问题扩展到 Vec<T> 和 &[T]。 - Shepmaster


答案:


如果是 String 和 Vec,他们做同样的事情。在 一般但是,它们并不完全相同。

首先,你必须明白 Deref。这种特性是在类型在逻辑上“包装”某些较低级别,更简单的值的情况下实现的。例如,所有“智能指针”类型(BoxRcArc) 实行 Deref 让您访问他们的内容。

它也是为了实现的 String 和 VecString “derefs”更简单 strVec<T> derefs更简单 [T]

写作 *s 只是手动调用 Deref::deref 转 s 进入“更简单的形式”。它几乎总是写的 &*s然而:虽然 Deref::deref 签名说它返回一个借来的指针(&Target),编译器插入第二个自动deref。这样就可以了,例如, { 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自动提供 String也是。 Rust不是继承而是继承 Deref 链接。

它的工作方式是,当您在值上请求特定方法时,Rust首先查看为该特定类型定义的方法。让我们说它找不到你要求的方法;在放弃之前,Rust会检查一下 Deref 实现。如果找到一个,则调用它然后 再试一次

这意味着当你打电话时 s.chars() 哪里 s 是一个 String, 什么是 其实 发生的是你正在打电话 s.deref().chars()因为 String 不  一个叫做的方法 chars但是 str   (向上滚动即可看到 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[..] 剥掉了 他们全部。这是因为没有 BoxRc&等等 实施 Index 特质,所以 Deref 链接导致呼叫 s.index(RangeFull) 链接所有这些中间层。

你应该使用哪一个?无论你想要什么。使用 &*s (要么 &**s, 要么 &***s)如果你想控制 究竟 你要剥离多少个间接层。使用 &s[..] 如果你想将它们全部剥离,只需获得值的最内层表示。

或者,你可以做我做的事情并使用它 &*s 因为它从左到右读,而 &s[..] 从左到右再次读取,这让我很烦。 :)

附录


9
2017-11-23 04:00



哇,谢谢您的深入评论! - chad


它们完全相同 String 和 Vec

[..] 语法导致调用 Index<RangeFull>::index() 而且它不只是糖 [0..collection.len()]。后者会引入绑定检查的成本。很高兴Rust的情况并非如此,所以他们都同样快。


相关代码:


6
2017-11-22 22:25