你很接近,但你错过了一个关键细节。
在Rust中,赋值(包括函数参数的赋值)通过移动该值进行,除非该值的类型实现了Copy
,此时会复制该值。在C++中,您必须使用std::move()
请求移动(在不是隐式情况下),而在Rust中,对于无法复制的值,移动是自动的,移动会使原变量为空,尝试再次读取会导致编译时错误。
Copy
特质被所有共享引用(&T
)实现,但不实现可变引用(&mut T
)
但是,在这种特殊情况下,编译器做了一件聪明的事情。如果移动了引用,则在调用func()
后,s
将处于“移动状态”,因此您可能会认为不能再使用它...但您可以!
func(s);
func(s);
这个可以编译。那么发生了什么?
编译器在此处插入了一个重新借用,就好像你写了这个一样:
func(&mut *s);
这种结构要求编译器不消耗引用s
,而是重新借用它的指向。 (当将引用传递给函数时,会隐式发生这种情况,但在其他情况下,它是不隐式的,必须显式地重新借用。)
但如果这样做,就会有两个可变引用指向同一个变量word
,违反了Rust定义的规则。
啊,但这并不是完全的规则。规则是,在任何给定的时刻,一个值可以是:
- 可以被任意数量的名称读取,或者
- 最多只能由一个名称写入。
这并不意味着您不能同时拥有两个可变引用指向同一个值,只是意味着一次只能使用一个。
在你的代码中,
main()
在重新借用
s
后将控制权转移到
func()
。这意味着在
s_ref
消失之前,
s
不能被使用 -- 但是在
s_ref
消失之前它根本就不可能被使用,因为必须在
func()
返回之后才能再次使用
s
。
因此,正如您所看到的,这并不违反 Rust 的别名规则。当您调用
func()
时,可以使用
s_ref
写入值
word
,但在函数返回之前不能使用
s
写入值。然后,
s
可以再次使用。
我们可以通过以下两个程序来演示这一点:
fn func(s_ref: &mut str) {}
fn main() {
let mut word = "hello".to_string();
let s: &mut str = &mut word;
let s2: &mut str = &mut *s;
func(s2);
func(s);
}
这个编译通过了!我们重新借用s
到s2
,但在停止使用s2
之前我们不使用s
。编译器会自动解决这个问题(参见非词法生命周期)。
然而,如果我们交错使用它们,就会出现问题:
fn func(s_ref: &mut str) {}
fn main() {
let mut word = "hello".to_string();
let s: &mut str = &mut word;
let s2: &mut str = &mut *s;
func(s);
func(s2);
}
现在编译器会抱怨:
error[E0499]: cannot borrow `*s` as mutable more than once at a time
--> src/main.rs:8:10
|
6 | let s2: &mut str = &mut *s;
| ------- first mutable borrow occurs here
7 |
8 | func(s);
| ^ second mutable borrow occurs here
9 | func(s2);
| -- first borrow later used here
在第 6 行和第 9 行之间,
s
和
s2
都被视为可用的,这是不允许的。
正如您所看到的,有多个可变引用指向同一值并没有问题。但是,它们的使用不允许重叠。只要它们不重叠,就不会出现问题。