可变借用互斥体内部的对象 - 如何重构?

Vit*_*meo 2 refactoring mutex rust borrowing

我的许多功能中都有以下模式:

use std::sync::{Arc, Mutex};

struct State { 
    value: i32
}

fn foo(data: Arc<Mutex<State>>) {
    let state = &mut data.lock().expect("Could not lock mutex");
    // mutate `state`
}
Run Code Online (Sandbox Code Playgroud)

&mut *data.lock().expect("Could not lock mutex") 一遍又一遍地重复,所以我想将它重构为一个函数,以便编写类似

let state = get_state(data); 
Run Code Online (Sandbox Code Playgroud)

我尝试了以下方法:

fn get_state(data: &Arc<Mutex<State>>) -> &mut State {
    &mut data.lock().expect("Could not lock mutex")
}
Run Code Online (Sandbox Code Playgroud)

哪个无法编译:

错误:无法返回引用临时值的值

这让我相信data.state.lock().expect("...")价值回报。但是,我可以看到状态通过在这个 playground 上的多次foo调用而发生变化。

这里发生了什么?为什么我看似简单的重构编译失败?


编辑:

我希望以下内容也能正常工作:

fn get_state<'a>(data: &'a Arc<Mutex<State>>) -> &'a mut State {
    let state: &'a mut State = &mut data.lock().expect("Could not lock mutex");
    state
}
Run Code Online (Sandbox Code Playgroud)

但它失败了:

   |
12 | fn get_state<'a>(data: &'a Arc<Mutex<State>>) -> &'a mut State {
   |              -- lifetime `'a` defined here
13 |     let state: &'a mut State = &mut data.lock().expect("Could not lock mutex");
   |                -------------        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use
   |                |
   |                type annotation requires that borrow lasts for `'a`
14 |     state
15 | }
   | - temporary value is freed at the end of this statement
Run Code Online (Sandbox Code Playgroud)

为什么从返回的任何内容的生命周期lockdata参数之一不匹配?

tre*_*tcl 6

抽象锁定和解锁互斥体的一种方法是 API 接受闭包并将未锁定的引用传递给它。

fn with_state<R>(data: Arc<Mutex<State>>, f: impl FnOnce(&mut State) -> R) -> R {
    let state = &mut data.lock().expect("Could not lock mutex");
    f(state)
}
Run Code Online (Sandbox Code Playgroud)

给定with_state,您可以写foo如下:

fn foo(data: Arc<Mutex<State>>) {
    with_state(data, |state| state.value += 1)
}
Run Code Online (Sandbox Code Playgroud)

这类似于 cratescrossbeam保证作用域线程始终加入的方式。它比返回 a 更严格,MutexGuard因为当您调用 时with_state,保证在闭包返回后删除防护。另一方面,返回 aMutexGuard更为通用,因为您可以编写with_state返回守卫的函数,但不能反其道而行之(用于with_state编写返回守卫的函数)。


Sve*_*rev 5

lock()方法返回MutexGuard而不是对受保护对象的直接引用。您可以使用对象引用,因为MutexGuard实现了Derefand DerefMut,但您仍然需要 mutex-guard 在范围内,因为当它超出范围时,互斥锁将被释放。此外,对 iner 对象的引用的生命周期与互斥保护的生命周期绑定,因此编译器将不允许您在没有互斥保护的情况下使用对内部对象的引用。

您可以在宏或方法中提取公共逻辑,但它应该返回 MutexGuard 而不是对内部对象的引用。