我的许多功能都具有以下模式:
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`
}
&mut *data.lock().expect("Could not lock mutex")
一遍又一遍地重复,因此我想将其重构为一个函数,以便编写类似
let state = get_state(data);
我尝试了以下操作:
fn get_state(data: &Arc<Mutex<State>>) -> &mut State {
&mut data.lock().expect("Could not lock mutex")
}
编译失败的地方:
错误:无法返回引用临时值的值
这使我相信data.state.lock().expect("...")
按值返回。但是,我可以看到状态通过多个on this 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
}
但是它失败了:
|
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
为什么从lock
返回的所有内容的生存期都与data
参数之一不匹配?
答案 0 :(得分:3)
lock()
方法返回MutexGuard
而不是直接引用受保护对象。您可以使用对象引用,因为MutexGuard
实现了Deref
和DerefMut
,但是您仍然需要互斥体保护罩在范围内,因为当互斥体超出范围时,互斥体锁将被释放。同样,对内部对象的引用的生存期也与互斥保护的生存期绑定在一起,因此编译器将不允许您在没有互斥保护的情况下使用对内部对象的引用。
您可以在宏或方法中提取常用逻辑,但是它应该返回MutexGuard而不是对内部对象的引用。
答案 1 :(得分:1)
一种抽象的锁定和解锁互斥锁的方法是让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)
}
给出with_state
,您可以按以下方式编写foo
:
fn foo(data: Arc<Mutex<State>>) {
with_state(data, |state| state.value += 1)
}
这类似于crossbeam
之类的板条箱确保有作用域的线程始终联接。它比返回MutexGuard
更为严格,因为当您调用with_state
时,保证在闭包返回后将删除防护。另一方面,返回MutexGuard
更为笼统,因为您可以根据返回防护的函数编写with_state
,但是不能反过来使用(使用{{1} }以编写返回防护的函数)。