How to mutate a Thread Local RefCell?

/*
1. Why does the second println! output print LocalKey{ .. }
   instead of the same output as the the first println! ?
2. If you remove the // below, why does it not compile ?
*/
use std::cell::RefCell;
use std::ops::DerefMut;

thread_local! { 
    static VEC: RefCell< Vec<f32> > = RefCell::new( vec![1.0, 2.0, 3.0] );
}

fn main() { 
    let v: RefCell< Vec<f32> > = RefCell::new( vec![1.0, 2.0] );
    v.borrow_mut( ).deref_mut().push(3.0);
    println!( "{:?}", v  );

    // VEC.borrow_mut( ).deref_mut().push(3.0);
    println!( "{:?}", VEC  );
}

When you use thread_local, you do not actually get an item of the specified type, you get an item of type LocalKey<_>. You have to use the methods of LocalKey to reach the actual value.

To get &RefCell from the LocalKey, you have to use with().

VEC.with(|tl_vec| {
    tl_vec.borrow_mut().push(3.0);
});

Or, there is also a combined version specifically for LocalKey<RefCell<_>>:

VEC.with_borrow_mut(|tl_vec| {
    tl_vec.push(3.0);
});

This callback pattern gives a couple of stronger guarantees than just returning a reference does:

  • It guarantees that the reference cannot outlive this specific call, which is necessary for soundness here, because otherwise you could take a reference to a thread-local, send it to another thread, wait for the thread to terminate, and end up with a dangling reference.
  • It guarantees that with() can run cleanup code after the callback function finishes. This doesn’t actually matter here, but it is critical for other uses of this callback pattern like std::thread::scope().

Because that's what LocalKey’s Debug implementation does. Why? I don’t know exactly. Perhaps there could be a problematic situation if the value is not yet initialized. You'll need to call with() before printing.

2 Likes

The program below works for me. Thanks.

use std::cell::RefCell;

thread_local! { 
    static VEC: RefCell< Vec<f32> > = RefCell::new( vec![1.0, 2.0] );
}

fn main() {
    let v: RefCell< Vec<f32> > = RefCell::new( vec![1.0, 2.0] );
    v.borrow_mut( ).push(3.0);
    println!( "{:?}", v.borrow()  );
    //
    VEC.with( |v|  v.borrow_mut().push(3.0) );
    VEC.with( |v| println!( "{:?}", v.borrow() ) );
}
1 Like