Lifetime difficulties with static global HashMap


#1

Hello Rust community,

I’m trying to do something unusual and because I begin with rust, I don’t manage to do it.

I try to do a library which creates a context for each thread.
Threads are managed by the application which uses my library.

I try to implement the following function:

struct Context {
    t: i32 
}

lazy_static! {
    // Since it's mutable and shared, use mutex
    static ref CONTEXTS: Mutex<HashMap<thread::ThreadId, Context>> = Mutex::new(HashMap::new());
}

fn get_context() -> &'static Context {
    let guard = CONTEXTS.lock().unwrap();
    let mut hashmap: HashMap<thread::ThreadId, Context> = *guard;
    let context = hashmap.get(&thread::current().id());
    match context {
        Some(context) => context,
        None => {
            let c = Context{t:99};
            hashmap.insert(thread::current().id(), c); 
            &hashmap.get(&thread::current().id()).unwrap()
        }   
    }   
}

What I want is simple: when you call get_context, the function returns the Context of this thread.
If the context doesn’t exist, the function creates the Context and returns it.

When I run this code, it doesn’t work, like you can see here:

Code

Could you help me to understand what is the proper way to implement this function ?
I tried to make it work for 5 hours without success.

Thanks in advance,
Sincerely,
Jean-Sébastien


#2

You can use a thread local for this. For example:

struct Context {
    t: i32,
}

thread_local! {
    static CONTEXTS: Context = Context {t: 99};
}

fn with_context<F: FnMut(&Context)>(f: F) {
    CONTEXTS.with(f);
}

fn main() {
    with_context(|c| println!("{}", c.t));
}

You cannot return the &'static Context however because that allows the reference to escape the thread localness. Instead, you can try inverting the API by requiring the caller to give you a closure that will receive temporary access to the context.

Alternatively, you can put the context into an Rc<Context> (or Rc<RefCell<Context>> if you need mutation of the context) and return a clone of that.


#3

Thanks for your answer, I will try to fully understand it !


#4

So after experimenting your solution, it works well for an immutable value, but is it possible to mutate the Context.t value ?

I did experimentation here but without success: https://play.rust-lang.org/?gist=545c9f1114959447e5fa496e0a9f5b7e&version=nightly


#5

Right - that’s where the RefCell comes in: Playground


#6

Thanks for your answer, I can see that I have a lot to learn… The path to master Rust seems long.
Nevertheless, community is here to help !