How to return a link to the subject of the value under the mutex?

I have a structure that looks something like this:

pub struct MyStruct { data: Arc<Mutex<HashMap<i32, Vec<i32>>>>, } 

I can easily get a mutex lock and request a basic HashMap :

 let d = s.data.lock().unwrap(); let v = d.get(&1).unwrap(); println!("{:?}", v); 

Now I want to create a method to encapsulate the request, so I am writing 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 obviously does not compile, because I'm trying to return a link to the data under the 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 this by wrapping the HashMap values ​​in 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 changing the data structure?

Full code example .

+7
rust
source share
2 answers

This solution is similar to @Neikos, but using owning_ref to navigate MutexGuard and a link to 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 is easy (using the map method on owning_ref ) to get a similar link to anything else available from Mutex (a separate element in Vec , etc.) without having to re-implement the return type.

+3
source share

This can be done using a secondary structure that implements Deref and contains MutexGuard.

Example:

 use std::sync::{Arc, Mutex, MutexGuard}; use std::collections::HashMap; use std::ops::Deref; pub struct Inner<'a>(MutexGuard<'a, HashMap<i32, Vec<i32>>>, i32); impl<'a> Deref for Inner<'a> { type Target = Vec<i32>; fn deref(&self) -> &Self::Target { self.0.get(&self.1).unwrap() } } pub struct MyStruct { data: Arc<Mutex<HashMap<i32, Vec<i32>>>>, } impl MyStruct { pub fn get_data_for<'a>(&'a self, i: i32) -> Inner<'a> { let d = self.data.lock().unwrap(); Inner(d, i) } } fn main() { let mut hm = HashMap::new(); hm.insert(1, vec![1,2,3]); let s = MyStruct { data: Arc::new(Mutex::new(hm)) }; { let v = s.get_data_for(1); println!("{:?}", *v); let x : Vec<_> = v.iter().map(|x| x * 2).collect(); println!("{:?}", x); // Just an example to see that it works } } 
+3
source share

All Articles