Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
558 views
in Technique[技术] by (71.8m points)

rust - How to return a reference to a sub-value of a value that is under a mutex?

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?

Full example code.

Question&Answers:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...