Programmer9000
Programmer9000

Reputation: 2181

Why can't a mutable variable be used when a mutable reference to that variable can?

I have a mutable string variable, and an immutable variable bound to a mutable reference to the mutable string variable.

let mut string = String::from("test");
let variable: &mut String = &mut string;
variable.push_str(" test");
string.push_str(" test");

This fails:

error[E0499]: cannot borrow `string` as mutable more than once at a time
 --> src/main.rs:5:5
  |
3 |     let variable: &mut String = &mut string;
  |                                      ------ first mutable borrow occurs here
4 |     variable.push_str(" test");
5 |     string.push_str(" test");
  |     ^^^^^^ second mutable borrow occurs here
6 | }
  | - first borrow ends here
  1. Without the second variable being mutable, why am I able to call push_str?
  2. Why am I able to call push_str on the second variable and not on the first?

Upvotes: 3

Views: 1521

Answers (2)

Vladimir Matveev
Vladimir Matveev

Reputation: 128061

You're getting this error because mutable borrowing is exclusive:

let mut string = String::from("test")
let variable = &mut string;

Here you create a mutable reference to a variable; because mutable references imply exclusive access, it is now impossible to access the original variable, because otherwise you would be violating aliasing guarantees.

Consider this:

let mut string = String::from("test");
{
    let variable = &mut string;
    variable.push_str(" test");
}
string.push_str(" test");

This code will compile and work as intended, because the mutable reference goes out of scope before the original variable is accessed again.

You can read more about this in the Rust book (see this link for the second edition of the book).

As for why you can call the mutating method on a non-mut variable, well, it is possible simply because the push_str() method accepts its receiver by &mut; if you already have &mut then it is used directly, but if you don't have one, then Rust will automatically try to create one for you, which is not possible if the variable is not mut:

let mut string = String::from("test");

string.push_str("test");
// equivalent to:
String::push_str(&mut string, "test");  // won't work if `string` is not `mut`

let variable = &mut string;
variable.push_str("test");
// [almost] equivalent to:
String::push_str(variable, "test");  // works because `variable` is already `&mut`

I wrote "almost" in the example above because in this case there is another step called reborrowing which basically ensures that the mutable reference can be used again after this call instead of being moved into the function call, but it doesn't really matter for this answer.

Upvotes: 5

8bittree
8bittree

Reputation: 1799

The Rust Book explains question 1 pretty well:

let mut x = 5;
let y = &mut x;

y is an immutable binding to a mutable reference, which means that you can’t bind 'y' to something else (y = &mut z), but y can be used to bind x to something else (*y = 5).

Basically, variable.push_str(" test"); is mutating the String that variable references, but it does not affect variable (i.e. the binding) itself.


The compiler error should explain question 2.

Upvotes: 3

Related Questions