The fact that Rust prevent two mutable references at the same time to prevent data races is a common misconception. This is only one of the reasons. Preventing two mutable references makes it possible to keep invariants on types easily and let the compiler enforce that the invariant are not violated.
Take this piece of C++ code for an example:
#include <vector>
int main() {
std::vector<int> foo = { 1, 2, 3 };
for (auto& e: foo) {
if (e % 2 == 0) {
foo.push_back(e+1);
}
}
return 0;
}
This is unsafe because you cannot mutate a vector while you are iterating it. Mutating the vector might reallocate its internal buffer, which invalidates all references. In C++, this is a UB. In Java or C# you would get a runtime exception.
Rust however, prevents this kind of issues at compile time:
fn main() {
let mut foo = vec![1, 2, 3];
for e in foo {
if e % 2 == 0 {
foo.push(e+1);
}
}
}
gives an error:
error[E0382]: borrow of moved value: `foo`
--> src/main.rs:6:13
|
2 | let mut foo = vec![1, 2, 3];
| ------- move occurs because `foo` has type `std::vec::Vec<i32>`, which does not implement the `Copy` trait
3 |
4 | for e in foo {
| ---
| |
| value moved here
| help: consider borrowing to avoid moving into the for loop: `&foo`
5 | if e % 2 == 0 {
6 | foo.push(e+1);
| ^^^ value borrowed here after move
Other languages, such as Python, manage internal buffers for you. While this avoid runtime errors, it this might still lead to unexpected result in some cases. For example in Python:
lst = ['a', 'b', 'c', 'd', 'e']
for x in lst:
lst.remove(x)
print(lst)
prints
['b', 'd']
The list was edited with no issues, but because the iterator had no knowledge of this, this lead to skipping some elements. Iterating with for x in list(lst) instead (making an explicit copy of the list so that the modified list and the iterator don't share any memory) yields [] as expected.
x1in your code? Where would you want to user2where you couldn't also just user1?