How to return a reference to a sub-value of a value that is under a mutex?
The parking_lot
crate provides an implementation of Mutexes that's better on many accounts than the one in std
. Among the goodies is MutexGuard::map
, which implements an interface similar to owning_ref
's.
use std::sync::Arc;
use parking_lot::{Mutex, MutexGuard, MappedMutexGuard};
use std::collections::HashMap;
pub struct MyStruct {
data: Arc<Mutex<HashMap<i32, Vec<i32>>>>,
}
impl MyStruct {
pub fn get_data_for(&self, i: &i32) -> MappedMutexGuard<Vec<i32>> {
MutexGuard::map(self.data.lock(), |d| d.get_mut(i).unwrap())
}
}
You can try it on the playground here.
This solution is similar to @Neikos's, but using owning_ref to do hold the MutexGuard
and a reference to the Vec
:
extern crate owning_ref;
use std::sync::Arc;
use std::sync::{Mutex,MutexGuard};
use std::collections::HashMap;
use std::vec::Vec;
use owning_ref::MutexGuardRef;
type HM = HashMap<i32, Vec<i32>>;
pub struct MyStruct {
data: Arc<Mutex<HM>>,
}
impl MyStruct {
pub fn new() -> MyStruct {
let mut hm = HashMap::new();
hm.insert(3, vec![2,3,5,7]);
MyStruct{
data: Arc::new(Mutex::new(hm)),
}
}
pub fn get_data_for<'ret, 'me:'ret, 'c>(&'me self, i: &'c i32) -> MutexGuardRef<'ret, HM, Vec<i32>> {
MutexGuardRef::new(self.data.lock().unwrap())
.map(|mg| mg.get(i).unwrap())
}
}
fn main() {
let s: MyStruct = MyStruct::new();
let vref = s.get_data_for(&3);
for x in vref.iter() {
println!("{}", x);
}
}
This has the advantage that it's easy (through the map
method on owning_ref
) to get a similar reference to anything else reachable from the Mutex
(an individual item in a Vec
, etc.) without having to re-implement the returned type.