Search code examples
rustlazy-static

How can return a reference to an item in a static hashmap inside a mutex?


I am trying to access a static hashmap for reading and writing but I am always getting error:

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()
}

But I am getting an error on the get function cannot return value referencing temporary value

I tried with .cloned(), .clone() or even nothing but I don't manage to get it to work. Can you help me?


Solution

  • I tried with .cloned(), .clone() or even nothing but I don't manage to get it to work. Can you help me?

    All Option::clone does is clone the underlying structure, which in this case is an &ModuleItem so it just clones the reference, and you still have a reference, which you can't return because you only have access to the hashmap's contents while you hold the lock (otherwise it could not work).

    Option::cloned actually clones the object being held by reference, but doesn't compile here because ModuleItem can't be cloned.

    First you have to return a Option<ModuleItem>, you can not return a reference to the map contents since the lock is going to be released at the end of the function, and you can't keep a handle on hashmap contents across mutex boundaries as they could go away at any moment (e.g. an other thread could move them, or even clear the map entirely).

    Then copy the ModuleItem, either by deriving Clone on ModuleItem (then calling Option::cloned) or by creating a new ModuleItem "by hand" e.g.

    pub fn get(identity_hash: i32) -> Option<ModuleItem> {
        MODULE_MAP.lock().unwrap().get(&identity_hash).map(|m| 
            ModuleItem { absolute_path: m.absolute_path.clone() }
        )
    }
    

    If you need to get keys out a lot and are worried about performances, you could always store Arc<ModuleItem>. That has something of a cost (as it's a pointer so your string is now behind two pointers) however cloning an Arc is very cheap.

    To avoid the double pointer you could make ModuleItem into an unsized type and have it store an str but... that's pretty difficult to work with so I wouldn't recommend it.