Creating a global, immutable manually initialized variable

I added this to my rust program:

static cell: OnceCell<String> = OnceCell::new();

This variable is supposed to be initialized manually when the program starts. But it fails to compile with an error saying "OnceCell can't be shared between threads". This variable is supposed to be immutable after it has been initialized. How do I implement this? lazy_static won't work because the value will be computed at runtime using environment variables and command line.

See OnceLock.

This type is a thread-safe OnceCell, and can be used in statics.

3 Likes

There is no need to use locks here because the data will not be modified after initialization.

in rust, globals must be thread safe, because the type system cannot guarentee only one thread is going to access them, so it must be conservative.

if your "global" is only going to be accessed in a single thread, you can use thread locals (instead of "true" global variables) which doesn't need to be thread safe.

otherwise, if you just don't want the proper safe solution suggested by @jumpnbrownweasel, for whatever reason, then you'll have to write some unsafe code (e.g. UnsafeCell<MaybeUninit<T>>), but the resposibility is on you to make sure the acess will not violate rust safety rules.

3 Likes

Does it need to be a global?
maybe you could do something with Box::leak to initialize it once and then read from multiple threads. Of course then you need to pass the pointer around to everything which could be inconvenient.

You can use std::thread_local! macro to use non-Syncable types (however, any possible worker threads won't be able to access this value, instead, they will see their own). If an application is going to be multi-threaded (and the value is immutable and effectively global), it may be better to use a global OnceLock or LazyLock than thread-local OnceCell or LazyCell, because in the latter case, the value has to be initialized just once while thread-local values have to be initialized for each thread separately.

OnceLock::get uses a single atomic load to check if if was initialized which is the least possible overhead in a multi-theaded environment.

6 Likes