In Rust, global variables are declared using the static keyword. Such variables live for the entire duration of the program. By default, access to immutable static is safe, but mutable access requires unsafe since the compiler cannot guarantee the absence of data races.
To safely access mutable global variables, special wrapper types are used: Mutex, RwLock, Atomic* types, or external crates (lazy_static, once_cell). Initialization is "lazy" if delayed object creation is required upon first access.
Examples:
static COUNTER: AtomicUsize = AtomicUsize::new(0); static ref CONFIG: Config = read_config(); // using lazy_static or once_cell
Can a mutable global variable be declared without special types if it will only be used from a single thread? Is it necessary to write unsafe?
Answer:
The compiler will require unsafe to be used for any modification of a global variable, even if the logic is single-threaded. This is a general requirement for all static mut. Only if the variable is wrapped in a synchronizing structure (Mutex, atomic, etc.), the compiler will allow safe access.
static mut VALUE: i32 = 0; unsafe { VALUE += 1; }
Story
In a web service, a regular static variable with a string was used to initialize the global cache. Several threads were writing to it concurrently, leading to memory "bits" and application crashes due to race conditions.
Story
In a command-line utility in Rust, a static variable was changed without synchronization because "it only works via main". Later, the code was adapted for multithreading, forgetting about the global mutable state, which caused very hard-to-detect bugs.
Story
In an embedded program, the static CONFIG was incorrectly initialized through a function at startup without guaranteeing it would be called exactly once. As a result, some parts of the code accessed uninitialized configuration (null reference).