如何在互斥锁内返回静态哈希表中的项的引用?

3

我试图访问一个静态哈希表用于读写,但是我总是收到错误:

use std::collections::HashMap;
use std::sync::Mutex;

pub struct ModuleItem {
    pub absolute_path: String,
}

lazy_static! {
    static ref MODULE_MAP: Mutex<HashMap<i32, ModuleItem>> = Mutex::new(HashMap::new());
}

pub fn insert(identity_hash: i32, module_item: ModuleItem) {
    MODULE_MAP
        .lock()
        .unwrap()
        .insert(identity_hash, module_item);
}

pub fn get(identity_hash: i32) -> Option<&'static ModuleItem> {
    MODULE_MAP.lock().unwrap().get(&identity_hash).clone()
}

但是在调用get函数时,出现了错误无法返回引用临时值

我尝试过使用.cloned().clone()甚至什么都不用,但我无法让它正常工作。你能帮帮我吗?


因为这个问题的性质有太多不同的微妙之处与链接的问题不适用,而且两组答案似乎有足够的区别来区分,所以我删除了这个重复目标 - E net4
2个回答

0
我尝试使用 .cloned()、.clone() 或者什么都不用,但是我无法让它工作。你能帮我吗?
Option::clone 只是克隆底层结构,而在这种情况下,底层结构是一个 &ModuleItem,因此它只是克隆引用,你仍然有一个引用,但你不能返回它,因为你只能在持有锁时访问哈希映射的内容(否则它将无法工作)。
Option::cloned 实际上是通过引用克隆所持有的对象,但在这里无法编译,因为 ModuleItem 无法被克隆。
首先,你必须返回一个 Option,你不能返回对映射内容的引用,因为锁将在函数结束时释放,你不能跨互斥体边界保持哈希映射内容的句柄,因为它们可能随时消失(例如,另一个线程可能会移动它们,甚至清除整个映射)。
然后复制 ModuleItem,可以通过在 ModuleItem 上派生 Clone(然后调用 Option::cloned)或手动创建一个新的 ModuleItem 来实现。
pub fn get(identity_hash: i32) -> Option<ModuleItem> {
    MODULE_MAP.lock().unwrap().get(&identity_hash).map(|m| 
        ModuleItem { absolute_path: m.absolute_path.clone() }
    )
}

如果您需要频繁地获取键并担心性能问题,您可以始终存储Arc<ModuleItem>。这会有一定的成本(因为它是一个指针,所以您的字符串现在在两个指针后面),但克隆Arc非常便宜。

为了避免双重指针,您可以将ModuleItem变成一个不定长类型,并将其存储为str,但是...这样做很难处理,所以我不建议这样做。


0
函数 get 无法使用静态生命周期,因为该数据不会在程序的整个生命周期中存在 (来自 Rust 书籍):

引用生命周期 'static 表示被引用的数据在整个运行程序的生命周期中存在。但它仍可强制转换为更短的生命周期。

所以你必须返回非静态引用或者 HashMap 的值的副本。引用不可能,因为 MODULE_MAP.lock().unwrap() 返回一个 MutexGuard,是一个临时变量,持有 HashMap。而 HashMapget() 返回一个引用。
由于临时的 MutexGuard 在函数结束时将被销毁,get 返回的引用将指向一个临时值。
为了解决这个问题,您可以使ModuleItem可克隆,并返回该值的副本:
use std::collections::HashMap;
use std::sync::Mutex;

#[derive(Clone)]
pub struct ModuleItem {
    pub absolute_path: String,
}

lazy_static::lazy_static! {
    static ref MODULE_MAP: Mutex<HashMap<i32, ModuleItem>> = Mutex::new(HashMap::new());
}

pub fn insert(identity_hash: i32, module_item: ModuleItem) {
    MODULE_MAP
        .lock()
        .unwrap()
        .insert(identity_hash, module_item);
}

pub fn get(identity_hash: i32) -> Option<ModuleItem> {
    MODULE_MAP.lock().unwrap().get(&identity_hash).cloned()
}

谢谢您提供的两个答案。它们似乎都有效。它们之间实际上有什么区别? - Alexandre
1
在另一个答案中,您必须通过“map”函数手动复制数据。在我的答案中,通过从“Clone”派生并使用“cloned”方法来完成复制。 - schrieveslaach

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接