Skip to content

Provide suggestion when creating static mut of type with internal mutability #151131

@estebank

Description

@estebank

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 default

Desired 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

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2024&gist=4434a1c05350f44776fcb9dfb7cb70c3

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 2850ca8295bc253186b2

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-diagnosticsArea: Messages for errors, warnings, and lintsA-lintsArea: Lints (warnings about flaws in source code) such as unused_mut.D-lack-of-suggestionDiagnostics: Adding a (structured) suggestion would increase the quality of the diagnostic.D-newcomer-roadblockDiagnostics: Confusing error or lint; hard to understand for new users.D-papercutDiagnostics: An error or lint that needs small tweaks.L-static_mut_refsLint: static_mut_refsT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions