我构造一个字符串,然后借用它进行一些更改。然后,我想查看字符串的更改方式,但是我无法打印出该值:
let mut s1 = String::from("hello");
let s2 = &mut s1;
s2.truncate(2);
print!("{}", s1);
println!("{}", s2);
error[E0502]: cannot borrow `s1` as immutable because it is also borrowed as mutable
--> src/lib.rs:5:18
|
3 | let s2 = &mut s1;
| ------- mutable borrow occurs here
4 | s2.truncate(2);
5 | print!("{}", s1);
| ^^ immutable borrow occurs here
6 | println!("{}", s2);
| -- mutable borrow later used here
我认为Rust中的借用与C ++类似,因此,s1
发生变化时,s2
也将发生相应的变化。
答案 0 :(得分:3)
Rust的引用无法像其他语言(如C / C ++ / Java)那样工作。 Rust编译器通过使用“借阅检查器”来确保编译时的内存安全。借阅检查器遵循一组规则,而您发布的代码违反了其中一个。
这是Rust书中的直接quote,它解决了这种确切情况:
- 在任何给定时间,您可以拥有一个可变引用或任意数量的不可变引用。
首先,您创建一个可变变量s1
,并通过s2
将其作为不可变变量借用。只要您不同时同时使用这两个功能,就可以了。这里没有发生此问题,因为您尚未真正使用引用进行任何操作。当您强制这两个引用同时处于活动状态时,会出现问题。当您在s1
超出范围之前(即最后一次使用之后)访问s2
时,会发生这种情况。看看这个:
let mut s1 = String::from("hello"); // -- Start of s1 scope --
let s2 = &mut s1; // -- Start of s2 scope --
s2.truncate(2); // s1 may not be used here as
// that breaks the rules
print!("{}", s1); // -- End of s1 scope --
println!("{}", s2); // -- End of s2 scope --
如您所见,由于代码的结构方式,s1
和s2
的范围必须同时处于活动状态。如果要交换最后两行代码,请将代码更改为:
let mut s1 = String::from("hello"); // -- Start of s1 scope --
let s2 = &mut s1; // -- Start of s2 scope --
s2.truncate(2); // s1 may not be used here as
// that breaks the rules
println!("{}", s2); // -- End of s2 scope --
print!("{}", s1); // -- End of s1 scope --
然后,您的代码将按预期编译并运行。原因是虽然s2
的作用域处于活动状态,但您根本没有使用s1
。换句话说,这些事情发生在上述代码的每一行:
s1
拥有新创建的String
s2
可变地借入String
s2
用于截断String
s2
用于打印String
。由于这是s2
的最后一次使用,因此在此行之后,String
的所有权将返回到s1
。s1
用于打印String
。我希望这可以为您澄清情况。
我建议您花些时间阅读Rust书的“了解所有权”一章here。我的建议是从头开始阅读整本书。它将使您很好地了解Rust作为一种语言及其生态系统。
答案 1 :(得分:0)
(我是问这个问题的人。)我喜欢@mrzenioszeniou的回答,不变的引用和可变的引用不能同时处于活动状态给我留下了深刻的印象。我也尝试了这段代码:
let mut s1 = String::from("hello");
{
let s2 = &mut s1;
s2.truncate(2);
} //--End of s2 scope
println!("{}", s1);
在这里,我将s2
作为&mut s1
带到一个新的子范围,当作用域结束时,它不会影响s1的不变借位。