繁体   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