ProgrammingSystem/backend and Embedded Developer

Tell us about the rules for working with global variables (static) in Rust. How is safety ensured during concurrent access, and what are the differences between various initialization and synchronization methods?

Pass interviews with Hintsage AI assistant

Answer

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

Trick Question

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; }

Examples of real errors due to unawareness of the topic's nuances


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).