如何返回对互斥锁下的值的子值的引用?

时间:2016-10-17 20:47:06

标签: rust

我的结构看起来有点像这样:

texture2D

我可以轻松锁定互斥锁并查询基础pub struct MyStruct { data: Arc<Mutex<HashMap<i32, Vec<i32>>>>, }

HashMap

现在我想创建一个封装查询的方法,所以我写了这样的东西:

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

这无法编译,因为我试图在impl MyStruct { pub fn get_data_for(&self, i: &i32) -> &Vec<i32> { let d = self.data.lock().unwrap(); d.get(i).unwrap() } } 下返回对数据的引用:

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(); | ^ 值包装在HashMap中来修复它,但它看起来很丑陋(Arc中的Arc)并使代码复杂化:

Arc

最好的方法是什么?是否有可能在不修改数据结构的情况下制作出我想要的方法?

Full example code

5 个答案:

答案 0 :(得分:5)

此解决方案与@ Neikos类似,但使用owning_ref执行MutexGuardVec的引用:

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);
    }
}

这样做的好处是很容易(通过map上的owning_ref方法)获得对Mutex中可以找到的任何其他内容的类似引用(Vec中的单个项目1}}等,而不必重新实现返回的类型。

答案 1 :(得分:3)

这可以通过使用实现Deref并保存MutexGuard的辅助结构来实现。

示例:

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
    }
}

答案 2 :(得分:2)

parking_lot板条箱提供了互斥量实现,在许多帐户上比在sink中的帐户更好。好东西中有std,它实现了类似于MutexGuard::map的接口。

owning_ref

You can try it on the playground here.

答案 3 :(得分:1)

Why can't I store a value and a reference to that value in the same struct?所述,Rental crate在某些情况下允许自引用结构。在这里,我们将ArcMutexGuard和值all捆绑到Deref到值的结构中:

#[macro_use]
extern crate rental;

use std::{
    collections::HashMap, sync::{Arc, Mutex},
};

use owning_mutex_guard_value::OwningMutexGuardValue;

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

impl MyStruct {
    pub fn get_data_for(&self, i: &i32) -> OwningMutexGuardValue<HashMap<i32, Vec<i32>>, Vec<i32>> {
        OwningMutexGuardValue::new(
            self.data.clone(),
            |d| Box::new(d.lock().unwrap()),
            |g, _| g.get(i).unwrap(),
        )
    }
}

rental! {
    mod owning_mutex_guard_value {
        use std::sync::{Arc, Mutex, MutexGuard};

        #[rental(deref_suffix)]
        pub struct OwningMutexGuardValue<T, U>
        where
            T: 'static,
            U: 'static,
        {
            lock: Arc<Mutex<T>>,
            guard: Box<MutexGuard<'lock, T>>,
            value: &'guard U,
        }
    }
}

fn main() {
    let mut data = HashMap::new();
    data.insert(1, vec![1, 2, 3]);
    let s = MyStruct {
        data: Arc::new(Mutex::new(data)),
    };

    let locked_data = s.get_data_for(&1);
    let total: i32 = locked_data.iter().map(|x| x * 2).sum();
    println!("{}", total);

    assert!(s.data.try_lock().is_err());

    drop(locked_data);

    assert!(s.data.try_lock().is_ok());
}

答案 4 :(得分:1)

这是评论中提到的闭包传递方法的实现:

impl MyStruct {
    pub fn with_data_for<T>(&self, i: &i32, f: impl FnOnce(&Vec<i32>) -> T) -> Option<T> {
        let map_guard = &self.data.lock().ok()?;
        let vec = &map_guard.get(i)?;
        Some(f(vec))
    }
}

Rust Playground

示例用法:

s.with_data_for(&1, |v| {
    println!("{:?}", v);
});
let sum: i32 = s.with_data_for(&1, |v| v.iter().sum()).unwrap();
println!("{}", sum);