簡體   English   中英

如何返回對 Hashmap 中包裝在 Rust 中的 Arc 和 Mutex 中的值的引用?

[英]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 認為你的代碼是錯誤的。

  1. 只有當您鎖定了受 Mutex 保護的值時,您才能與它進行交互。
  2. 由 RAII 警衛處理的鎖。

所以,我對你的代碼脫糖:

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 個。

  1. 無法返回引用臨時值的值
  2. 返回引用當前 function 擁有的數據的值

讓我們一個一個地深入挖掘其中的每一個。

第一個問題是告訴我們創建了臨時值,而返回值引用了它。

這里的臨時值是指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 內。 每當您通過它們時,您都需要將您的值帶入鎖中。

為什么返回參考不是一個好主意並且不受 rust 支持?

因為,當我們在 #1 處進行鎖定控制時,返回的值是當時值的快照。 假設有一種方法可以某種方式返回引用,然后如果任何其他線程獲取鎖並更新值。 現在,這只能發生...

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM