如何从Arc包装的Hashmap和Rust包装的Mutex返回对值的引用?

如何从Arc包装的Hashmap和Rust包装的Mutex返回对值的引用?,rust,Rust,我在返回HashMap中的值引用时遇到了一些麻烦,该值由Arc和Mutex包装,用于在线程之间共享。代码如下所示: 使用std::sync::{Arc,Mutex}; 使用std::collections::HashMap; 结构嘿{ a:弧形 } 恳求嘿{ fn获取(&self,键:&String)->&String{ self.a.lock().unwrap().get(key.unwrap()) } } 如上所示,代码未能编译,因为返回一个引用当前函数所拥有的数据的值。我知道lock(

我在返回
HashMap
中的值引用时遇到了一些麻烦,该值由Arc和Mutex包装,用于在线程之间共享。代码如下所示:


使用std::sync::{Arc,Mutex};
使用std::collections::HashMap;
结构嘿{
a:弧形
}
恳求嘿{
fn获取(&self,键:&String)->&String{
self.a.lock().unwrap().get(key.unwrap())
}
}

如上所示,代码未能编译,因为
返回一个引用当前函数所拥有的数据的值。我知道
lock()
返回一个局部变量MutexGuard。但是我如何实现这种方法来获取HashMap中的值的引用呢。如果我不能,生锈的动机是什么?

您需要克隆弧并将克隆弧移动到另一个线程/任务。从克隆中,您可以锁定并访问它。如果访问多于写入,我建议使用
RwLock
而不是互斥锁

克隆弧时,不是克隆基础对象,而是克隆弧。同样,在您的情况下,您需要将结构包装成圆弧或更改设计,因为应该克隆和移动的是圆弧


我相信分享物品的方法应该是通过警卫。使用RWLock,多人可通过防护装置读取地图:

use async_std::task;
use std::sync::{Arc,RwLock, RwLockReadGuard, RwLockWriteGuard};
use std::collections::HashMap;

#[derive(Default)]
struct Hey{
    a:Arc<RwLock<HashMap<String, String>>>
}

impl Hey {      

    pub fn read(&self) -> RwLockReadGuard<'_, HashMap<String, String>> {
        self.a.read().unwrap()
    }

    
    pub fn write(&self) -> RwLockWriteGuard<'_, HashMap<String, String>> {
        self.a.write().unwrap()
    }    
}


fn main() {
    let h = Hey{..Default::default()};

    h.write().insert("k1".to_string(), "v1".to_string());
   
    println!("{:?}", h.read().get("k1"));
    task::block_on(async move {
        println!("{:?}", h.read().get("k1"));
    });

} 
使用async_std::task;
使用std::sync::{Arc、RwLock、RwLockReadGuard、rwlockriteguard};
使用std::collections::HashMap;
#[衍生(默认)]
结构嘿{
a:弧形
}
impl Hey{
pub fn read(&self)->RwLockReadGuard{
self.a.write().unwrap()
}    
}
fn main(){
设h=Hey{..Default::Default()};
h、 write().插入(“k1.to_string(),“v1.to_string());
println!(“{:?}”,h.read().get(“k1”);
任务::块打开(异步移动){
println!(“{:?}”,h.read().get(“k1”);
});
} 

让我解释一下为什么rustc认为您的代码是错误的

  • 只有在锁定了互斥锁保护的值时,才能与该值进行交互
  • 由RAII guard操作的锁
  • 所以,我要删除你的代码:

    fn get(&self,key:&String)->&String{
    让lock=self.a.lock().unwrap();
    let reference=lock.get(key.unwrap();
    放下(锁);//释放你的锁
    //我们返回对不受互斥保护的数据的引用!
    //有人可以从hashmap中删除项目,您将读取已删除的数据
    //免费后使用是UB,所以禁止
    返回参考;
    }
    
    您可能需要使用
    作为值:

    #[derive(Default)]
    struct Hey{
        a:Arc<RwLock<HashMap<String, Arc<String>>>>
    }
    fn get(&self,key:&String)->Arc<String>{
        self.a.lock().unwrap().get(key).unwrap().clone()
    }
    
    #[派生(默认)]
    结构嘿{
    a:弧形
    }
    fn get(&self,key:&String)->Arc{
    self.a.lock().unwrap().get(key.unwrap().clone())
    }
    
    附言。
    另外,您可以使用
    Arc
    (我建议这样做),这样可以避免额外的指针间接指向。它可以从字符串中构建:
    let-arc:arc=my_String.into()
    Arc::from(my_string)

    如果允许该返回类型指向互斥锁的数据内部,则不会阻止其他代码锁定互斥锁并删除条目,这意味着返回的引用将指向已解除分配的内容。@loganfsmyth是的,您是对的。如果允许的话,地图可能会因为来自另一个线程的操作而改变,它是UB。我想你没听清我的话。编译错误是因为
    Mutex
    中的
    lock()
    返回一个临时变量,其生存期属于
    get
    函数的范围。因此它不能返回引用。现在,这并不是由于线程的原因,我相信使用getfn-ref到hashmap,然后将inside-ref返回到string;相反,我分享了我找到守卫的方法。这将允许同时阅读,因为这是很好的!假设读一次,然后忘记这种操作。然而,这里关于一致性的假设是,获取hashmap值的引用(字符串的Arc克隆)的线程可以一直指向hashmap中不再存在的值。i、 另一个线程可以改变映射,并在hashmap中放置另一个弧。