Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to return a reference to an Rc<RefCell<HashMap<K, V>> value?

I'm trying to learn Rust and I'm having some problems with different smart pointers and stuff.

Here is my code:

pub struct MyMap<T> {
    map: Rc<RefCell<HashMap<String, T>>>,
}

impl <T> MyMap<T> {
    // Not entire sure if it's supposed to be Option<Ref<T>> or something else here.
    pub fn get(&self, key: &str) -> Option<Ref<T>> {
        todo!("What do I do here?")
    }
}

The closest I've got is by searching the HashMap twice:

impl <T> MyMap<T> {
    pub fn get(&self, key: &str) -> Option<Ref<T>> {
        if self.map.borrow().contains_key(key) {
            Some(Ref::map(self.map.borrow(), |m| m.get(key).unwrap()))
        } else {
            None
        }
    }
}

Which isn't very elegant to say the least.

like image 254
Gal Avatar asked Nov 14 '25 19:11

Gal


1 Answers

Two solutions that come to my mind:

  • Ref::filter_map, stabilized in 1.63.0.
  • Use a context manager pattern, which circumvents the entire problem.

filter_map:

use std::{
    cell::{Ref, RefCell},
    collections::HashMap,
    rc::Rc,
};

pub struct MyMap<T> {
    map: Rc<RefCell<HashMap<String, T>>>,
}

impl<T> MyMap<T> {
    pub fn get(&self, key: &str) -> Option<Ref<'_, T>> {
        Ref::filter_map(self.map.borrow(), |map| map.get(key)).ok()
    }
}

fn main() {
    let map: MyMap<u32> = MyMap {
        map: Rc::new(RefCell::new(HashMap::from([
            ("meaning".to_string(), 42),
            ("nice".to_string(), 69),
        ]))),
    };

    println!("{:?}", map.get("meaning"));
}
Some(42)

Context manager:

The idea here is that instead of returning a reference, you pass in a closure of the action that needs the value. That entirely circumvents the lifetime problem, because the variables inside of the get (or with_value in the example below) are still in scope while the closure gets executed.

use std::{cell::RefCell, collections::HashMap, rc::Rc};

pub struct MyMap<T> {
    map: Rc<RefCell<HashMap<String, T>>>,
}

impl<T> MyMap<T> {
    pub fn with_value<F, O>(&self, key: &str, f: F) -> O
    where
        F: FnOnce(Option<&T>) -> O,
    {
        f(self.map.borrow().get(key))
    }
}

fn main() {
    let map: MyMap<u32> = MyMap {
        map: Rc::new(RefCell::new(HashMap::from([
            ("meaning".to_string(), 42),
            ("nice".to_string(), 69),
        ]))),
    };

    map.with_value("meaning", |value| {
        println!("{:?}", value);
    });
}
Some(42)
like image 125
Finomnis Avatar answered Nov 17 '25 18:11

Finomnis



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!