Why can one use a mutable reference but not a second one?

Hello,

I have a function

fn main() {
	let mut s = String::from("hello");

    let r1 = &mut s;
    //let r2 = &mut s;
    s.push_str(" hello");
    println!("{}", r1);
}

and want to know why it is allowed to have a mutable reference where I can create a "race condition" like this but not allowed to have another mutable reference.

This code isn't allowed. Rust Playground

error[E0499]: cannot borrow `s` as mutable more than once at a time
 --> src/main.rs:6:5
  |
4 |     let r1 = &mut s;
  |              ------ first mutable borrow occurs here
5 |     //let r2 = &mut s;
6 |     s.push_str(" hello");
  |     ^ second mutable borrow occurs here
7 |     println!("{}", r1);
  |                    -- first borrow later used here

For more information about this error, try `rustc --explain E0499`.

yes because I created a race condition. I want to know why it is not allowed to have 2 mutable references (probably to prevent race conditions) but with one mutable reference I can also create this code you linked above that is basically a race condition.

What is the alternate version you're talking about?

This may answer your question.

&mut _ would be more accurately called exclusive references than mutable. A core tenet of Rust's borrowing system is that &mut _ are exclusive -- while active, there can be no other active shared references (&_) to the same place, exclusive references to the same place, etc.

The exclusivity helps prevent a number of memory related bugs such as use-after-free, not just data races. For example the call to push_str may cause a reallocation of the Strings buffer, making pre-existing references like r1 and r2 dangle.

The exclusivity is enforced in a more general way than reasoning about specific circumstances. In the OP, r2 is denied for the same reason the non-commented code fails: calling s.push_str creates a &mut s, and using s in that way isn't compatible with keeping pre-existing references alive (be they shared or exclusive).

There are actually two mutable references in this code, although one is "hidden":

    let r1 = &mut s;
    s.push_str(" hello");

The push_str method takes &mut self, so s.push_str("hello") is shorthand for:

    String::push_str(&mut s, " hello");

If you compile the expanded code then the error might be a bit easier to see

error[E0499]: cannot borrow `s` as mutable more than once at a time
 --> src/main.rs:5:22
  |
4 |     let r1 = &mut s;
  |              ------ first mutable borrow occurs here
5 |     String::push_str(&mut s, " hello");
  |                      ^^^^^^ second mutable borrow occurs here
2 Likes
fn main() {
	let mut s = String::from("hello");

    let r1 = &mut s;
    //let r2 = &mut s;
    s.push_str(" hello");
    println!("{}", r1);
}

Your code uses a mutable reference, r1, which is defined for the first time. Then it prints within the println function. There are many lines in between, and you've made several mutable references to the variable s. This means that between the start of r1 and its use in println, there are many unpredictable things happening in the Rust compiler. For instance, in the middle part of the code, you continue to reference s and modify its value. It's also possible that you put r2's mutable reference inside a thread, but you don't call the thread. Or perhaps you put it inside a thread and then execute the thread. These situations could lead to unexpected behavior. The middle portion may encounter some unsafe circumstances.

For example, if you introduce multiple threads in the middle portion, each holding its own mutable reference to s and attempting to perform an operation of adding 1 to the value 1000 times, this would pose a problem. The value of s isn't protected, resulting in different outcomes with each run. Additionally, the timing of when the threads will execute is uncertain. If s is manually deallocated before their execution, it will certainly cause issues. You could even pass these threads around. This doesn't just create a race condition; it's more than just a simple race condition.

Another example is if we don't use threads. In the middle portion of the code, other operations are performed, such as modifying its memory. You might think that by default, since the initial value is "hello," using the replace method to change it to "world" and then printing it should be valid. After all, this is possible in other programming languages. However, Rust doesn't allow this because you've created a variable and used a mutable reference for the first time. The idea behind creating a mutable reference is that the value might need to be modified or undergo other operations. It seems reasonable to print it. However, creating a second mutable reference isn't appropriate. You can entirely use the first mutable reference to make modifications. Creating a second mutable reference implies an expectation to modify the value as well, which is problematic. The creation of the second mutable reference suggests an intent to modify the value, impacting the operation of the first mutable reference. This is illogical. While it's technically possible, from a safety perspective, you cannot guarantee what will happen with the second mutable reference. There's even a concern that the second mutable reference might release or modify the value of the first mutable reference.

Therefore, in this scenario, it's necessary to restrict multiple mutable references. During the lifetime of a mutable reference, there shouldn't be anything else capable of modifying the value.

wow thanks for the long answer!