无法在共享引用后面移动

4

这段代码无法编译:

fn main() {
    let x = "".to_string();
    let y = &x;
    let z = *y;
}

编译器错误输出为:

无法移动共享引用后面的 *y

移动发生的原因是 *y 具有类型 String,它没有实现 Copy 特性

我不太清楚这里发生了什么,需要解释一下。
我原本希望 z 拥有这个 String 的所有权,使得 xy 变得无法使用。

1
为什么你期望 z 接管 String 的所有权?你可以从拥有的内容中借用,但是你不能从借用的内容中拥有一个对象。 - Ömer Erden
@ÖmerErden 借款人 y 在执行完 *y*yx 后,当其所有权转移到 z 时,x 就没有借款人了?但这似乎不是 Rust 的工作方式。我猜想(没有证据)在整个语句完成之前,y 仍然在作用域内。 - TSK
*y并不完全等同于xx仍然存在并由当前函数的作用域拥有。*y,即*&x,允许您访问指向内存位置(&x)中的值。如果您尝试进行赋值,则会尝试移动该值,但该值的所有者是x。如果Rust允许您移动它,那么x会发生什么? - Ömer Erden
2个回答

3
let z = *y;

这行代码只知道 y,而 y 是一个 &String&String 不包含任何关于它来自哪里的信息,它只包含它是一个指向 String 的引用的信息。它不知道也不关心 x 包含的实际内容,除了借用检查器确保 x 保持在作用域内且不可变之外,它对 x 没有任何控制权。
因此,*y 实际上并没有生成 x,而是一个匿名的 String 值,只能通过引用访问,这意味着它可以使用,但不是所有者。
通过执行 z = *y,您正在尝试拥有引用后面的值。但正如我所说,这将需要修改 x(因为之后它不再有效),而 yx 没有任何权限。因此,这是不可能的。
因为对于可复制类型,执行 z = *y 并不是问题,因为它们不需要转移所有权,而只需进行复制,Rust 通知您这是不可能的,因为 y 引用的值没有实现 Copy

1
我想理解这个问题的关键是 *y 不同于 x。但是 y 并不是在栈上取 x 的地址作为自己的值吗?我一直在考虑 C/C++ 中的指针,但 Rust 强制实施了自己的所有权和借用语义? - TSK
1
是的,它存储指针。但 Rust 不仅像 C/C++ 一样类型安全,它还具有所有权安全性,这是许多新 Rust 程序员所苦恼的概念。因此编译器必须确保存储的地址实际上是有效的。在 Rust 中还有原始指针,它们没有任何限制,但需要使用 unsafe 关键字。 - Finomnis
1
@TSK 这里需要理解的重要概念是,Rust 没有任何未定义行为,因为黑客/蠕虫的大部分攻击面都基于未定义行为。这听起来很简单,但对语言的结构有着重大的影响,比如你刚刚遇到的借用规则。 - Finomnis
@TSK 理论上你是对的,借用检查器可能可以变得足够聪明,以理解z = *y实际上影响了x变量。只是现在还没有达到那个程度。编写借用检查器是一项艰苦的工作,在你的特定情况下,做*y毫无意义,因为你可以简单地做z = x。而在所有其他情况下,当你无法轻松访问原始变量x时,绝对不要允许人们移出y - Finomnis
1
就算不值一提,这个“匿名的String值”被称为一个_占位符_。 - Chayim Friedman

-1

我有不同的理解。但是我的理解可能是错误的。 通过解引用运算符(*y),会生成一个无名临时变量。这个临时变量与 x(let x = "".to_string();)不同,它不获取所有权。 (let z = *y;)=> 移动了临时变量,但没有所有者。所以出现错误。


https://rustwiki.org/en/reference/expressions.html#moved-and-copied-types


1
这似乎只是在术语上与被接受的答案有所不同,但你的术语并不正确:被创建的东西不叫做“临时”,而是一个“地方”。 - Chayim Friedman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接