'If two mutable references aren’t allowed simultaneously, why are they allowed at all?
According to the documentation,
Data races cause undefined behavior and can be difficult to diagnose and fix when you’re trying to track them down at runtime; Rust prevents this problem by refusing to compile code with data races!
Why is this a problem at all unless you’re multithreading? And wouldn’t this problem persist with simultaneously accessing a variable and its one mut
reference?
Solution 1:[1]
You seem to be confused about some things.
First, you seem to be under the impression that if you mutably borrow from a value, you can mutate the value through the mutable reference and directly using the variable that you borrowed from. This isn't true. The variable is effectively unusable until the borrow ends:
fn main() {
let mut x = 0;
let y = &mut x;
// The following line causes a compile-time error.
// error[E0506]: cannot assign to `x` because it is borrowed
x = 1;
*y = 2;
}
There can be at most one name through which a value can be modified at any given moment. Since x
was mutably borrowed by y
, the name x
can't be used to modify the value until the borrow by y
is released.
Why is this a problem at all unless you’re multithreading?
Because mutating state can invalidate references.
Consider the case where you have a Vec. You are not allowed to modify the Vec or any of its contents while the Vec or any of its elements are borrowed because that's unsafe. Why would it be unsafe in single-threaded code?
What happens if you have a reference to an item and that item is removed from the Vec? You have a reference to either an invalid value or a different value than you did before.
What happens if you add an element to the Vec, but its internal array is full? The Vec will allocate a new, larger array, transfer all of the elements from the smaller array into the larger array, then destroy the smaller array. All previously-existing references to elements would be invalidated by this operation.
This is why you can't mutate a Vec while you iterate it, for example:
for item in some_vec.iter() {
some_vec.push(item.clone());
}
Vec::push()
requires mutably borrowing the Vec, but iteration requires immutably borrowing the Vec. You can't do both at the same time.
Rust doesn't let you do these things because they are fundamentally unsound operations. The entire purpose of the Rust language is to catch exactly these sorts of problems at compile time.
Note that this is also unsafe in C++, for example -- adding an element to an std::vector
might invalidate all existing references (in the case of a reallocation), which can cause undefined behavior when you attempt to use an invalidated reference.
The difference is that C++ will accept this dangerous code. In C++, it is the programmer's job to check for memory safety, and if you mess it up you have a potentially disastrous bug, or even a severe security vulnerability.
This is why Rust was created -- to take this critical task that humans aren't good at, and have the compiler do it for you.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 |