-
-
Notifications
You must be signed in to change notification settings - Fork 14.5k
Description
Code
use std::sync::Mutex;
static mut STDINOUT_MUTEX: Mutex<bool> = Mutex::new(false);
fn main() {
let mut _lock = unsafe { STDINOUT_MUTEX.lock().unwrap() };
}Current output
error: creating a shared reference to mutable static
--> src/main.rs:4:30
|
4 | let mut _lock = unsafe { STDINOUT_MUTEX.lock().unwrap() };
| ^^^^^^^^^^^^^^^^^^^^^ shared reference to mutable static
|
= note: for more information, see <https://doc.rust-lang.org/edition-guide/rust-2024/static-mut-references.html>
= note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives
= note: `#[deny(static_mut_refs)]` (part of `#[deny(rust_2024_compatibility)]`) on by defaultDesired output
error: creating a shared reference to mutable static
--> src/main.rs:4:30
|
4 | let mut _lock = unsafe { STDINOUT_MUTEX.lock().unwrap() };
| ^^^^^^^^^^^^^^^^^^^^^ shared reference to mutable static
|
= note: for more information, see <https://doc.rust-lang.org/edition-guide/rust-2024/static-mut-references.html>
= note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives
= help: use a type that relies on "interior mutability" instead; to read more on this, visit <https://doc.rust-lang.org/reference/interior-mutability.html>
= note: `#[deny(static_mut_refs)]` (part of `#[deny(rust_2024_compatibility)]`) on by default
help: `Mutex` already provides "interior mutability" to modify its contents, so its binding doesn't need to be declared as mutable
|
2 - static mut STDINOUT_MUTEX: Mutex<bool> = Mutex::new(false);
2 + static STDINOUT_MUTEX: Mutex<bool> = Mutex::new(false);
|Rationale and extra context
It is not necessarily obvious that static mut shouldn't be used and what to use instead, but more importantly, when the user is already using a type that doesn't need to be mut, they might not immediately notice.
This has been spotted in the wild: https://aus.social/@jpm/115891840629307599
Other cases
static mut STATE: LazyLock<GlobalState> = LazyLock::new(|| {
GlobalState
});
fn main() {
unsafe { STATE.example() };
}Lint doesn't trigger for
static mut COUNTER: u64 = 0;
fn main() {
unsafe { COUNTER = 1 };
}Rust Version
1.94.0-nightly
2026-01-13 2850ca8295bc253186b2Anything else?
The documentation https://doc.rust-lang.org/edition-guide/rust-2024/static-mut-references.html#mutex-or-rwlock already mentions:
When your type is more complex than an atomic, consider using a Mutex or RwLock to ensure proper access to the global value.
// Change from this: // static mut QUEUE: VecDeque<String> = VecDeque::new(); // to this: static QUEUE: Mutex<VecDeque<String>> = Mutex::new(VecDeque::new()); fn main() { QUEUE.lock().unwrap().push_back(String::from("abc")); let first = QUEUE.lock().unwrap().pop_front(); }
But we should provide suggestions for every case mentioned in that document. I notice that the lint doesn't trigger for static mut COUNTER: u64 = 0;, which should suggest using atomics.