为什么 Rust 会阻止多重可变引用?

Why Rust prevents from multiple mutable references?

和主题一样,为什么 Rust 会阻止多重可变引用?我已经阅读了 rust-book 中的章节,并且我知道当我们拥有多线程代码时,我们可以避免数据竞争,但让我们看一下这段代码:

fn main() {
    let mut x1 = String::from("hello");
    let r1 = &mut x1;
    let r2 = &mut x1;

    r1.insert(0, 'w');

}

此代码不会同时 运行,因此不会出现数据竞争。 更重要的是,当我创建新线程并且我想在新线程中使用来自父线程的变量时,我必须移动它,所以只有新线程是父变量的所有者。

我能看到的唯一原因是程序员在成长过程中会迷失在他的代码中。我们有多个地方可以修改一条数据,即使代码不是 运行 并行的,我们也会遇到一些错误。

Rust 同时阻止两个可变引用以防止数据竞争这一事实是一个常见的误解。这只是其中一个原因。防止两个可变引用可以轻松地保持类型的不变量,并让编译器强制不违反不变量。

以这段C++代码为例:

#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;
}

这是不安全的,因为您不能在迭代向量时改变它。改变向量可能会重新分配其内部缓冲区,这会使所有引用无效。在 C++ 中,这是一个 UB。在 Python、Java 或 C#(可能还有大多数其他语言)中,您会遇到运行时异常。

然而,Rust 在编译时防止了此类问题:

fn main() {
    let mut foo = vec![1, 2, 3];
    
    for e in foo {
        if e % 2 == 0 {
            foo.push(e+1);
        }
    }
}

报错:

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

这个限制的最大好处是 rust 可以防止编译时的数据竞争。如果我们有两个指针指向同一块数据,并且其中一个指针用于写入数据,并且没有同步这些指针之间的数据访问的机制,则会发生数据竞争。在那种情况下,你可以想象一个指针将读取数据,而在中间,另一个指针将修改数据。在那种情况下,我们将恢复损坏的数据。要修复此错误,您可以将这些引用切换回不可变引用。

Rust 强制执行“单个写入者或多个读取者”规则:您可以读取和写入值,或者它可以是 由任意数量的读者共享,但不能同时共享。