1

I construct a string, then borrow it to do some changes. I then want to see how the string is changed, but I cannot print out the value:

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

I thought borrowing in Rust is similar to C++ so that when s1 is changed s2 will be changed correspondingly.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Gu Xianyu
  • 13
  • 3
  • 1
    I don't understand what is your real issue. Since you borrowed `s1` into `s2`, use `s2` to print the value just as you did. – Boiethios Sep 24 '19 at 07:24
  • 1
    Swap the last two lines around. That way you won't borrow a value that is already mutably borrowed. – Peter Hall Sep 24 '19 at 08:00
  • although not recommended or is a good solution (unless you know what you are upto), using [unsafe](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=7aae2ab45a7856478c62dac04bc8fad5) is one way of working around, if you really want to maintain the order of the statements as is. – vikram2784 Sep 24 '19 at 21:47

2 Answers2

6

Rust's references do not work like they would in other languages like C/C++/Java. The Rust compiler ensures memory safety at compilation time, and it does so through the use of the "borrow checker". The borrow checker adheres to a set of rules, and the code you posted violates one of them.

Here's a direct quote from the Rust book that addresses this exact situation:

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

First you create a mutable variable s1, and borrow it as an immutable via s2. This is fine, so long as you're not using both of them simultaneously. The problem doesn't occur here, because you haven't really done anything with the references yet. The problem occurs when you force these two references to be active at the same time. This happens when you access s1 before s2 goes out of scope(that would be after it's last use). Take a look at this:

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

As you can see, due to the way your code is structured, the scopes of s1 and s2 must be active at the same time. If you were to swap the last two lines of code, changing your code into this:

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

Then your code would compile and run as expected. The reason is that while s2's scope is active, you're not using s1 at all. In other words, these things happen at every line of the above code:

  1. s1 owns newly the created String
  2. s2 mutably borrows the String
  3. s2 is used to truncate the String
  4. s2 is used to print the String. Since this is the last use of s2, after this line ownership of the String goes back to s1.
  5. s1 is used to print the String.

I hope this clarifies the situation for you.

I'd suggest that you take the time to look at the Rust book's "Understanding Ownership" chapter here. My advice would be to go through the whole book starting from the beginning. It will give you a very good understanding of Rust as a language and its ecosystem.

  • Your explanation does not really explain why when you remove either the `print!` or the `println!` the example compiles. It is not forbidden to take an immutable borrow of a mutable value; only to take an immutable borrow of a value *that is also mutably borrowed* -- `s1` is not already borrowed at the point where `s2` is initialized. The problem comes later when you try to borrow `s1` a second time before `s2`'s lifetime ends. – trent Sep 24 '19 at 13:31
  • Right you are @trentcl. I've updated my answer to try and explain this. – mrzenioszeniou Sep 25 '19 at 15:35
  • +100 I want to upvote this explanation more than once. This would be a great example to have in the Rust book. – jmrah Nov 06 '19 at 11:56
  • Why does the error message say, "immutable borrow occurs here" on line 5 of the error message? What is trying to borrow s1 immutably? println!()? – jmrah Nov 07 '19 at 00:56
  • How does `println!("{}", s2);` end the scope of `s2`? If I do, say, `s2.truncate(1);` after the `println!` call, Rust is perfectly ok with it, so after the `println!` I still have access to `s2`. – lurker Dec 01 '22 at 21:38
0

(I am the one who asked the question.)I love @mrzenioszeniou 's answer and I am impressed by immutable reference and mutable reference cannot be active at the same time. I also tried this code:

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

Here I bring s2 as &mut s1 to a new child-scope, when scope ends, it won't affect s1's immutable borrow.

Gu Xianyu
  • 13
  • 3
  • 3
    This works without the `{}` to force a new scope, although in older versions of Rust the `{}` was necessary (see ["non-lexical lifetimes"](https://stackoverflow.com/questions/50251487/what-are-non-lexical-lifetimes)). The difference between this code and the code in the question is the second use of `s2`. – trent Sep 25 '19 at 16:05