如何在可变借用后打印出一个值?

How to print out a value after a mutable borrow?

我构造了一个字符串,然后借用它做一些修改。然后我想看看字符串是如何改变的,但我无法打印出值:

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 将相应改变。

Rust 的引用不像在 C/C++/Java 等其他语言中那样工作。 Rust 编译器在编译时确保内存安全,它通过使用 "borrow checker" 来实现。借用检查器遵守一套规则,而您发布的代码违反了其中一个规则。

这是来自 Rust 书籍的直接 quote,它解决了这种确切 情况:

  • At any given time, you can have either one mutable reference or any number of immutable references.

首先创建一个可变变量 s1,然后通过 s2 将其作为不可变变量借用。这很好,只要您不同时使用它们。这里没有出现问题,因为您还没有真正对引用做任何事情。当您强制这两个引用同时处于活动状态时,就会出现问题。当您在 s2 超出范围之前访问 s1 时会发生这种情况(那将是在它最后一次使用之后)。看看这个:

  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 --

如您所见,由于代码的结构方式,s1s2 的范围必须同时处于活动状态。如果您要交换最后两行代码,请将您的代码更改为:

  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。换句话说,这些事情发生在上面代码的每一行:

  1. s1 拥有新创建的 String
  2. s2 可变借用 String
  3. s2用于运行分类String
  4. s2用于打印String。由于这是 s2 的最后一次使用,在此行之后 String 的所有权将返回到 s1.
  5. s1用于打印String.

我希望这能为您澄清情况。

我建议您花时间看看 Rust 书的 "Understanding Ownership" 章节 here。我的建议是从头开始阅读整本书。它会让您很好地理解 Rust 作为一种语言及其生态系统。

(我是问这个问题的人。)我喜欢@mrzenioszeniou 的回答,我对 不可变引用和可变引用不能同时激活 印象深刻.我也试过这段代码:

   let mut s1 = String::from("hello");
    {
        let s2 = &mut s1;
        s2.truncate(2);
    } //--End of s2 scope
   println!("{}", s1);

这里我把s2作为&mut s1带到一个新的子作用域,当作用域结束时,它不会影响s1的不可变借用。