Initialize Rc<T> type lazily, and only once

#1

So I have a type RcTree, which behind the screens uses an Rc<T> which means it’s not shareable between threads. I want to initialize an RcTree value on first use, then reuse the same value, and maybe clone new references to it within one thread.

Because the type is not sharable between threads, it looks like using lazy_static is off the table. And because it’s a rather complex type, I can’t put it in a top-level const.
Is there any other way I can init the value only once rather than pay the allocation and initialization costs on every use?

#2

thread_local!?

2 Likes
#3

The API is somewhat clunkier than the one lazy_static! has (why doesn’t std::thread::LocalKey have a Deref impl to &T?), but otherwise it does precisely what I want.
Thanks!

#4

A Deref impl would give you a &'static T, which would be unsound since you could send it to another thread while the source thread is destroyed, along with the T itself.

#5

I see why the lifetime of the borrow would be 'static.
But I don’t understand why it would automatically follow that that borrow would be shareable between threads: the thread_local! macro explicitly creates a type that disallows this inter-thread sharing, therefore I wouldn’t expect any borrow deriving from that to be shareable either. Could you please explain why the shareability between threads does follow?

#6

Deref just returns a &T. How would a &'static T-that-is-actually-thread-local be differentiated by the type system from a &'static T-that-isnt-a-thread-local?

#7

You can:

  • get used to the unergonomic thread_local!,

  • use Arc and lazy_static! if the ergonomics of the code are more important than its performance;

  • make your different structs carry a reference to your “pseudo-global” Rc, which can then be created as a classic variable before borrowing it to your structs;

  • (if I have inferred your purpose correctly, which I may very well not) use Option<RcTree> to be able to define your leafs as None instead of cloning a global leaf sentinel value;

EDIT: If I am correct about my assumption and you prefer your leaf sentinels to Nones, note that in such case thread_local! becomes more ergonomic to use (thanks to eta reduction):

use ::std::rc::Rc;

type RcTree = Rc<(/* ... */)>;

thread_local!{
    static LEAF: RcTree = Rc::new({
        /* ... */
    });
}

fn main ()
{
    let yet_another_leaf: RcTree = LEAF.with(Rc::clone);
}
#8

I like once_cell for such cases. It’s just an object, not some kind of global macro, so you can put it anywhere.

#9

Interesting crate!

I guess that answers my main question about it: why not just use the macro? :slight_smile: