[英]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.