Lazily initialized static variable with random number generator


Hi lovely helpful people! :slight_smile:

I have made a simple crate for generating random “lorem ipsum” text: lipsum. It uses a Markov chain internally to generate the text. Currently, the code use the thread-local random number generator directly when it needs some randomness. It looks similar to this:

let mut rng = rand::thread_rng();
let next = rng.choose(next_words).unwrap();

I would like to store the RNG in the MarkovChain struct instead. That way you can seed the RNG and get consistent output when needed.

However, I am also initializing a static MarkovChain value with lazy_static! — this is to save the overhead of training the chain every time a bit of lorem ipsum text is needed.

Expanding the MarkovChain struct to

pub struct MarkovChain<'a> {
    pub map: HashMap<Bigram<'a>, Vec<&'a str>>,
    pub rng: rand::ThreadRng, // <-- the new struct field

gives me this lovely error message when compiling the code:

error[E0277]: the trait bound `std::rc::Rc<std::cell::RefCell<rand::reseeding::ReseedingRng<rand::StdRng, rand::ThreadRngReseeder>>>: std::marker::Sync` is not satisfied in `MarkovChain<'static>`
   --> src/
213 | / lazy_static! {
214 | |     /// Markov chain generating lorem ipsum text.
215 | |     static ref LOREM_IPSUM_CHAIN: MarkovChain<'static> = {
216 | |         let mut chain = MarkovChain::new();
...   |
220 | |     };
221 | | }
    | |_^ `std::rc::Rc<std::cell::RefCell<rand::reseeding::ReseedingRng<rand::StdRng, rand::ThreadRngReseeder>>>` cannot be shared between threads safely
    = help: within `MarkovChain<'static>`, the trait `std::marker::Sync` is not implemented for `std::rc::Rc<std::cell::RefCell<rand::reseeding::ReseedingRng<rand::StdRng, rand::ThreadRngReseeder>>>`
    = note: required because it appears within the type `rand::ThreadRng`
    = note: required because it appears within the type `MarkovChain<'static>`
    = note: required by `lazy_static::lazy::Lazy`
    = note: this error originates in a macro outside of the current crate

The code in question. I believe I roughly understand what the error says: the ThreadRng RNG is not thread safe (not Sync) and this is required by the voodoo done by the lazy_static! macro.

Does anybody have a good tip for this situation?

I don’t intend to share the MarkovChain values between threads — I would just like them to carry a bit of state. It doesn’t sound like a hard problem :slight_smile:


Can you use thread_local!?


Wow, yes, I can indeed! With the help of a RefCell I managed to get the behavior I was looking for. Thanks a lot! :slight_smile:

Now I wonder a bit what the advantage of lazy_static! is… perhaps that it gives you a variable that you can treat more or less like the original type due to the Deref trait?


That’s one (I actually think Deref is much nicer, see The other advantage is, of course, that it results in a global static.