Static singleton instance of HashMap and returning reference to stored values

I am very new to Rust. I have a specific problem that has really tied me down. Hoping to get some help from the experts. I will use a simplified case to present the problem.

I have a Products struct that I use to some product information in a HashMap. The HashMap is a singleton static instance that is threadsafe. I used dashmap::DashMap instead of Mutex<HashMap<>>.

Here is the code I implemented.

struct Products {
    items: &'static DashMap<String, Product>,
}

fn products() -> Products {
    static INSTANCE: SyncOnceCell<DashMap<String, Product>> = SyncOnceCell::new();
    Products {
        items: INSTANCE.get_or_init(|| DashMap::new()),
    }
}

impl Products {
    fn add(&mut self, product: Product) {
        self.items.insert(product.name.clone(), product);
    }

    fn get(&self, name: &str) -> Option<Product> {
         //This is the function I am having a very difficult time writing.
    }
}

// For the testing purposes I am using a very simple Product struct. My actual use would have very
// complex things to be stored here.
struct Product {
    name: String,
    price: f64,
}

I am unable to write the get() method for Products. I have tried a large number of ways, but none seems to work.

It looks like it is very hard to return a reference to a value stored in a HashMap from a function in Rust. Hoping this is a solved problem.

Really appreciate any help.

Of course it is not – but:

  1. You are not returning a reference, are you? The signature of your get() function says: -> Option<Product> – no references in sight in the return type.

  2. Clearly, you can't just return a reference into a data structure that synchronizes access to its elements, because how would it know how long you are accessing the element for? It has to return something that calls back to the owning structure when dropped.

    As you observed yourself, DashMap is equivalent to a lock/mutex containing a regular HashMap. Accordingly, the signature of DashMap::get() tells you that it doesn't – it can't – return a regular reference. Just like Mutex::lock() returns a MutexGuard, the DashMap::get() method returns a Ref, which is an RAII guard wrapping a reference. When it's dropped, the map knows you don't use the referenced value anymore.

Consequently, while you could return a reference from a regular HashMap, you can't do so with DashMap. You either have to duplicate the signature of DashMap::get() and return a Ref, or clone the returned value (incidentally, the latter approach matches the current signature of your get() function).

2 Likes

Thank you very much for the prompt response.

If I were to change the signature of the get() method to return Option<&Product> like

fn get(&self, name: &str) -> Option<&Product> {
         //This is the function I am having a very difficult time writing.
 }

I am still not sure how to accomplish this? Sorry for being so clueless here.

You can't, that's the whole point. You have to return an Option<Ref<'_, Product>> (docs).

Thank you.