It’s because globals are, well, global in scope. Global variables don’t know how many threads of execution your program will have, so there needs to be some kind of scheme in place to make sure that access to them is safe. A Mutex is one such construct that will do the trick, but you could also rig up some other type that’s lighter in weight and unsafe impl Sync for it. Then it would be on you to make sure that your impl is actually safe.
Is lazy_static the correct approach?
It works, but is it designed for multi threading (so inappropriate in my case) and there is another way to have a global structure?
Maybe you’d like thread_local! better? You can think of this like a global in a single-threaded app, and it doesn’t have a Sync requirement on the type. You still need something like RefCell if you want mutability though.
This is in a recursive function so locking it is very problematic. I can change the design so the locks do not collide (I think) but that is a lot of cognitive overhead
Is there a reason to not use thread_local! in this case as sfackler mentioned? It’ll never lock (it only ever panics - and it allows multiple concurrent reads), and it has less overhead because it isn’t a lock.
If you are sure you’re single threaded, then thread_local! will also live as long as you need it to.
If you’re trying to mutably access the value from multiple places (even within a single thread), Rust will not allow you to do that. Even if you use a RefCell instead of Mutex, it will return an error when you try to borrow the value without releasing the previous borrow. So if this is the case, you’ll have to rethink your design and avoid multiple simultaneous borrows.
Note that making immutable simultaneous borrows is fine. RWLock will allow you to do it in multi-threaded context (so it will work in a static variable), and a thread-local RefCell will also work.
Global state belongs in global variables.
I am using one thread. thread_local! is working well. There is a bit of fluff… VAR.with(|f|){fo_some_thing_with(&*f.borrow()} instead of do_some_thing_with(&VAR).
Passing a value around would touch every function signature, in some cases quite tangentially to what the function actually does.
What I am doing here is storing objects in a global array (Vector) and describing the relationships between them with the usize indexes into the array. This means I can do al sorts of things copying the index that the borrow checker would not let me do with the objects themselves.
Agreed about the fluff. I usually wrap access to thread locals like this with a pair of functions if the thread local is common enough. Should at least make it a bit better?
fn with_xxx<O, F>(func: F) -> O
where
F: FnOnce(&Xxx) -> O
{
XXX.with(|refcell| func(refcell.borrow()))
}
fn with_xxx_mut<O, F>(func: F) -> O
where
F: FnOnce(&mut Var) -> O
{
XXX.with(|refcell| func(refcell.borrow_mut()))
}
Then at least it’s with_var(|f| fo_some_thing_with(f)) or with_var(fo_some_thing_with) rather than the whole incantation every time.
But I think that’s about as good as global state gets in rust - you could make a macro to automatically do the with_var(|f| ) but that would be a bit overdoing it and would obscure what’s actually happening.