为什么我可以重新分配移动变量?

时间:2020-10-14 17:13:55

标签: rust move-semantics

我发现此代码可以编译:

let mut s1 = String::from("hello");
let _s2 = s1;
s1 = String::from("world");
println!("{}", s1);

如果第三行不存在,我将收到一条错误消息,抱怨无法借用已移动的变量,这对我来说是可以理解的:

error[E0382]: borrow of moved value: `s1`
 --> src/main.rs:5:16
  |
2 | let mut s1 = String::from("hello");
  |     ------ move occurs because `s1` has type `std::string::String`, which does not implement the `Copy` trait
3 | let _s2 = s1;
  |           -- value moved here
4 | //s1 = String::from("world");
5 | println!("{}", s1);
  |                ^^ value borrowed here after move

用第三行代码进行编译。

这是我的理解:s1移动之后,s1仍处于某种有效状态,只是编译器阻止了您对其进行访问;如果您重新分配它,它将获得新的所有权,并且编译器会理解它,因此可以进行编译。

我是对的吗?在哪里可以找到正式解释此问题的任何文档/规范?

1 个答案:

答案 0 :(得分:5)

s1的原始值已被移动,因此尝试使用s1是非法的-直到它被赋予新值为止。

这是我的理解:s1移动之后,s1仍处于某种有效状态,只是编译器阻止了您对其进行访问;如果您重新分配它,它将获得新的所有权,并且编译器会理解它,因此可以进行编译。

这是无效的。现在移动s1时,绑定将绑定到未初始化的内存,因此完全使用它是非法的。赋予它新的值意味着该内存不再是未初始化的,并且可以安全地再次使用。

与以下各项进行比较:

let mut s1; 
s1 = String::from("world");
println!("{}", s1);

第一行,s1的状态与移入原始代码后的状态完全相同。

请注意,编译器可以选择对第二个值使用不同的内存地址。不允许对原始值进行引用,因此,除非您开始打印原始指针,否则完全看不到此实现细节。

您可能由此推断出,重新分配给移动的可变绑定在语义上等同于引入一个新的绑定,该绑定将第一个绑定起来。这几乎是正确的,但事实并非如此。绑定的删除顺序与引入的顺序相反,这里的删除顺序反映了原始绑定的顺序。