[英]How to return a reference to a value from Hashmap wrappered in Arc and Mutex in Rust?
我在返回HashMap<String,String>
中的值引用时遇到了一些麻烦,该引用由 Arc 和 Mutex 包装以在线程之间共享。 代码是这样的:
use std::sync::{Arc,Mutex};
use std::collections::HashMap;
struct Hey{
a:Arc<Mutex<HashMap<String, String>>>
}
impl Hey {
fn get(&self,key:&String)->&String{
self.a.lock().unwrap().get(key).unwrap()
}
}
如上所示,代码编译失败,因为returns a value referencing data owned by the current function
。 我知道lock()
返回 MutexGuard 这是一个局部变量。 但是我怎样才能实现这种方法来获得对 HashMap 中的值的引用。 如果我不能,那么 Rust 禁止这个的动机是什么?
让我解释一下为什么 rustc 认为你的代码是错误的。
所以,我对你的代码脱糖:
fn get(&self,key:&String)->&String{
let lock = self.a.lock().unwrap();
let reference = lock.get(key).unwrap();
drop(lock); // release your lock
// We return reference to data which doesn't protected by Mutex!
// Someone can delete item from hashmap and you would read deleted data
// Use-After-Free is UB so rustc forbid that
return reference;
}
可能您需要使用Arcs
作为值:
#[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()
}
PS 另外,您可以使用Arc<str>
(我建议这样做),这将使您免于额外的指针间接。 它可以从 String 构建: let arc: Arc<str> = my_string.into();
或Arc::from(my_string)
您需要克隆 ARC 并将克隆 ARC 移动到另一个线程/任务。 您可以从克隆中锁定和访问它。 如果访问次数多于写入次数,我建议使用RwLock
而不是 Mutex。
当您克隆 ARC 时,您不会克隆底层 object 只是 ARC。 同样在您的情况下,您需要将结构包装到 ARC 中或更改设计,因为应该克隆和移动 ARC
我相信分享 object 的方法应该是通过守卫。 使用 RWLock 多个可以通过警卫读取 map:
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"));
});
}
TLDR;
因为,您做出了包装数据的设计决定,即HashMap<String, String>
in Arc<Mutex<..>>
我假设您需要以线程安全的方式跨线程/任务共享这些数据。 这是此设计选择的主要用例。
因此,我对今天阅读本文的任何人的建议不是对这个问题的直接回答(返回参考),而是更改设计, get
您使用.to_owned()
方法返回拥有的数据.
fn get(&self, key: &String) -> String {
let lock = self.a.lock().unwrap(); // #1 Returns MutexGuard
let val = lock.get(key).unwrap();
val.to_owned()
}
长表
在截断的原始代码中,实际上手头有 2 个问题,尽管问题中只提到了 1 个。
让我们一个一个地深入挖掘其中的每一个。
第一个问题是告诉我们创建了临时值,而返回值引用了它。
这里的临时值是指MutexGuard
。 lock 方法不会返回对 HashMap 的引用,而是围绕MutexGuard
包裹的HashMap
。 .get()
在 MutexGuard 上工作的原因是因为它实现DeRef::deref
trait。 本质上,这意味着 MutexGuard 可以在需要时取消引用到它包装的值。 当我们调用 `.get()
我们可以更好地理解它
fn deref<'a>(&'a self) -> &'a T
使用 Arc 和 Mutex 的整个想法是添加在多个线程之间安全地更新数据的能力。 这种线程安全性由Mutex
提供,它在包装数据上启用锁定机制,在您的情况下HashMap
。
正如@Abhijit-K 所指出的,引用锁的 scope 之外的任何值都不是一个好的设计。 正如@Angelico 在帖子中很好地解释的那样,锁被丢弃在 function 的 scope 内。 每当您通过它们时,您都需要将您的值带入锁中。
因为,当我们在 #1 处进行锁定控制时,返回的值是当时值的快照。 假设有一种方法可以以某种方式返回引用,然后如果任何其他线程获取锁并更新值。 现在,这只能发生...
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.