I have a structure that looks somewhat like this:
pub struct MyStruct {
data: Arc<Mutex<HashMap<i32, Vec<i32>>>>,
}
I can easily get a lock on the mutex and query the underlying HashMap
:
let d = s.data.lock().unwrap();
let v = d.get(&1).unwrap();
println!("{:?}", v);
Now I want to make a method to encapsulate the querying, so I write something like this:
impl MyStruct {
pub fn get_data_for(&self, i: &i32) -> &Vec<i32> {
let d = self.data.lock().unwrap();
d.get(i).unwrap()
}
}
This fails to compile because I'm trying to return a reference to the data under a Mutex
:
error: `d` does not live long enough
--> <anon>:30:9
|
30 | d.get(i).unwrap()
| ^
|
note: reference must be valid for the anonymous lifetime #1 defined on the block at 28:53...
--> <anon>:28:54
|
28 | pub fn get_data_for(&self, i: &i32) -> &Vec<i32> {
| ^
note: ...but borrowed value is only valid for the block suffix following statement 0 at 29:42
--> <anon>:29:43
|
29 | let d = self.data.lock().unwrap();
| ^
I can fix it by wrapping the HashMap
values in an Arc
, but it looks ugly (Arc
in Arc
) and complicates the code:
pub struct MyStruct {
data: Arc<Mutex<HashMap<i32, Arc<Vec<i32>>>>>,
}
What is the best way to approach this? Is it possible to make a method that does what I want, without modifying the data structure?
This can be made possible by using a secondary struct that implements
Deref
and holds the MutexGuard.Example:
As described in Why can't I store a value and a reference to that value in the same struct?, the Rental crate allows for self-referential structs in certain cases. Here, we bundle the
Arc
, theMutexGuard
, and the value all into a struct thatDeref
s to the value:This solution is similar to @Neikos's, but using owning_ref to do hold the
MutexGuard
and a reference to theVec
:This has the advantage that it's easy (through the
map
method onowning_ref
) to get a similar reference to anything else reachable from theMutex
(an individual item in aVec
, etc.) without having to re-implement the returned type.