【问题标题】:Why Rust prevents from multiple mutable references?为什么 Rust 会阻止多个可变引用?
【发布时间】:2020-02-10 08:55:24
【问题描述】:

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

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

    r1.insert(0, 'w');

}

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

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

【问题讨论】:

  • 每个帖子只能问一个问题。我建议您编辑您的帖子以删除第二个问题并在自己的帖子中提出。
  • 好的,我做到了。这是链接:stackoverflow.com/q/58367174/9620900如果你想回答。
  • 在代码中对x1 进行多个可变引用有什么好处?您想在哪里使用r2 而不能只使用r1
  • 整个 Java 按照我上面描述的方式工作。您可以在同一范围内拥有数百万个包含对同一数据段的引用的对象,还是我错了?

标签: multithreading rust borrow-checker borrowing


【解决方案1】:

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

【讨论】:

  • 您是否有任何来自官方文档的参考来支持“防止两个可变引用使得可以轻松地保持类型上的不变量并让编译器强制不违反不变量。” ?
  • @Izana 答案给出了一个示例,其中 C++ 允许多个引用,包括一个可变引用,该引用已损坏,并表明 Rust 直接等效项无法编译。
  • 当然。我的意思是,“不变量”可能与编译器的算法有关。您是否参考过在编译器设计期间专门选择了此不变量?当您提到“防止数据竞争是一种常见的误解”时,我怀疑让编译器算法变得容易被认为更重要。
  • 为什么 java 会因此而失败。新的内部缓冲区意味着引用本身被写入新位置。但是引用仍然指向正确的堆位置。什么都不会失效。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-23
  • 1970-01-01
相关资源
最近更新 更多