Why `static mut` can be shared between threads safely? (but without `mut` it can't)

In this code (also on playground), I get no errors during compile:

#![allow(dead_code)]

static FORMAT_STRING: &[u8; 3] = b"%s\0";
static mut FORMAT_STRING_PTR: *const u8 = FORMAT_STRING.as_ptr();

fn main() {
    println!("Hello, world!");
}

except if I remove the mut, then I get the error:

Compiling playground v0.0.1 (/playground)
error[E0277]: `*const u8` cannot be shared between threads safely
 --> src/main.rs:4:28
  |
4 | static  FORMAT_STRING_PTR: *const u8 = FORMAT_STRING.as_ptr();
  |                            ^^^^^^^^^ `*const u8` cannot be shared between threads safely
  |
  = help: the trait `Sync` is not implemented for `*const u8`
  = note: shared static variables must have a type that implements `Sync`

For more information about this error, try `rustc --explain E0277`.
error: could not compile `playground` (bin "playground") due to 1 previous error

Why is it safe with mut but isn't without it? It doesn't seem to make sense. It seems like with mut it should be less safe.

As a side note: I don't want those 2 vars to be mut, I want them static, pre-main() initialized(hopefully not on heap, but ok either way), so I can use them as readonly. (though presumably unsafe code could mutate that string (well, the 3 bytes) if it has pointer to it? and I wouldn't know)

Thanks!

$ rustc -vV
rustc 1.76.0-nightly (07dca489a 2024-02-04) (gentoo)
binary: rustc
commit-hash: 07dca489ac2d933c78d3c5158e3f43beefeb02ce
commit-date: 2024-02-04
host: x86_64-unknown-linux-gnu
release: 1.76.0-nightly
LLVM version: 17.0.6

$ cargo -vV
cargo 1.76.0-nightly
release: 1.76.0-nightly
host: x86_64-unknown-linux-gnu
libgit2: 1.7.1 (sys:0.18.1 vendored)
libcurl: 8.7.1 (sys:0.4.70+curl-8.5.0 system ssl:rustls-ffi/0.12.2/rustls/0.22)
os: Gentoo Linux 2.15 [64-bit]

Static mut's are not required to be Sync but statics are, so the premise of the question is a bit backwards - the static mut is not "safer", it just has different trait bounds.

https://doc.rust-lang.org/reference/items/static-items.html

For one time initialization, look into OnceCell and OnceLock which provide a more convenient interface over writing static muts yourself (requires unsafe).

1 Like

You can use a static without unsafe, but you can't use a static mut without unsafe. So static has stricter preconditions.

If OnceCell and OnceLock don't meet your needs, look into lazy_static.

1 Like

No, static mut — just like static without mut — can't be shared between threads safely without additional synchronization.

It compiles in more cases only because static mut has fewer safeguards than the regular static. It's a dangerous feature, and Rust will deprecate in the near future.

4 Likes