【问题标题】:Mutable borrow fail in Mutex [duplicate]Mutex中的可变借用失败[重复]
【发布时间】:2021-08-31 09:29:05
【问题描述】:

我收到关于 Mutex 中可变借用失败的错误。

这里是代码。 现在好了。但是如果取消注释这 2 行,它将无法编译,这会将 state 包装到 Mutex 中并将其解包出来。

为什么 Mutex 会有所作为?

use std::collections::HashMap;
use std::sync::Mutex;

struct State {
    h1: HashMap<String, String>,
    h2: HashMap<String, String>,
}

fn main() {
    let mut state = State {
        h1: HashMap::new(),
        h2: HashMap::new(),
    };

    // It fails to compile if uncommenting these 2 lines!
    //let state = Mutex::new(state);
    //let mut state = state.lock().unwrap();

    let v1 = state.h1.get_mut("abc").unwrap();
    let v2 = state.h2.get_mut("abc").unwrap();
    v1.push_str("123");
    v2.push_str("123");
}

取消注释这两行的错误:

error[E0499]: cannot borrow `state` as mutable more than once at a time
  --> tmp.rs:20:14
   |
19 |     let v1 = state.h1.get_mut("abc").unwrap();
   |              ----- first mutable borrow occurs here
20 |     let v2 = state.h2.get_mut("abc").unwrap();
   |              ^^^^^ second mutable borrow occurs here
21 |     v1.push_str("123");
   |     -- first borrow later used here

===编辑===

如果更改一些代码顺序就可以了:

    let state = Mutex::new(state);
    let mut state = state.lock().unwrap();

    let v1 = state.h1.get_mut("abc").unwrap();
    v1.push_str("123");
    let v2 = state.h2.get_mut("abc").unwrap();
    v2.push_str("123");

似乎v1 拥有state 的引用。如果v1 结束,它会丢弃引用,然后state 可以再次借用。

为什么v1 持有state 的引用?

====编辑====

@Alexey Larionov 给出了答案。我找到了一个类似的question。我应该通过deref_mut() 手动将state 从 MutexGuard 中取出。所以添加这一行就可以了:

    let state = state.deref_mut();

【问题讨论】:

  • 如果你足够勇敢(或鲁莽!)你可以实现借用拆分stackoverflow.com/a/68581212/2588800。否则,您可以执行@Netwave 建议的操作或使用“内部可变性”

标签: rust borrow-checker


【解决方案1】:

只是为了详细说明@Netwave 的答案,以防没有Mutex 你不会收到这样的错误,因为这样做

let mut state = State {
        h1: HashMap::new(),
        h2: HashMap::new(),
};
let v1 = state.h1.get_mut("abc").unwrap();
let v2 = state.h2.get_mut("abc").unwrap();

v1 可变借用state.h1v2 可变借用state.h2,它们都算作state 的不可变借用,以防止将state 移出(例如let state2 = state; 而存在其他借用)。由于所有的借用规则都得到满足(要么每个对象最多一个可变借用每个对象的多个不可变借用),因此允许借用。

Mutex 的情况下这一行

let mut state = state.lock().unwrap();

生成MutexGuard::&lt;State&gt; 类型的state。通过这种方式使用它

let v1 = state.h1.get_mut("abc").unwrap();

Rust 不会在 MutexGuard::&lt;State&gt; 中找到 h1,这就是为什么它会使用 DerefMut 特征来隐式地从中获取 &amp;mut State。这就是你可变借用state 的地方。由于您稍后对 v2 使用了类似的构造,因此您将尝试在 state 上获得第二个可变借用,从而收到错误

【讨论】:

  • 非常感谢。我不能像@Netwave 建议的那样做,因为我必须先获取 v1 和 v2(以确保密钥存在),然后再更新它们。有没有其他方法让我像state 一样处理Mutex&lt;state&gt;
  • 也许只是if (state.h1.contains_key("abc") &amp;&amp; state.h2.contains_key("abc")) { /* Netwave's solution */}if 条件应该不可变地借用 state
【解决方案2】:

通过这样做,您可以对状态本身进行 2 个可变引用。要解决它,您可以重新调整 get_mut 调用的范围:

fn main() {
    let mut state = State {
        h1: HashMap::new(),
        h2: HashMap::new(),
    };

    // It fails to compile if uncommenting these 2 lines!
    let state = Mutex::new(state);
    let mut state = state.lock().unwrap();

    {
        let v1 = state.h1.get_mut("abc").unwrap();
        v1.push_str("123");
    }

    let v2 = state.h2.get_mut("abc").unwrap();
    v2.push_str("123");
}

这样,当你得到第二个引用时,第一个已经被删除了。

Playground

【讨论】:

  • 谢谢。即使没有 {} 块也没关系。我在原始问题上附加了一些代码。但我还是想知道原因。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-03-05
  • 1970-01-01
  • 1970-01-01
  • 2011-12-10
  • 1970-01-01
  • 1970-01-01
  • 2019-08-12
相关资源
最近更新 更多