# Pre-RFC: Deprecate then remove \`static mut\`

**URL:** <https://internals.rust-lang.org/t/pre-rfc-deprecate-then-remove-static-mut/20072>\
**Category:** language design\
**Created:** [December 23, 2023, 8:37am UTC](https://internals.rust-lang.org/t/pre-rfc-deprecate-then-remove-static-mut/20072 "2023-12-23T08:37:02Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![dyslexicsteak](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/dyslexicsteak/32/11627_2.png) [@dyslexicsteak](https://internals.rust-lang.org/u/dyslexicsteak)\
**Post date:** [December 23, 2023, 8:37am UTC](https://internals.rust-lang.org/t/pre-rfc-deprecate-then-remove-static-mut/20072/1 "2023-12-23T08:37:02Z")

</div>

# Summary

Deprecate usage of `static mut` for the 2024 edition of Rust, directing users to switch to interior mutability with subsequent removal of the syntax entirely in the 2027 edition. (This is not pertinent to &'static mut)

# Motivation

The existing `static mut` feature is difficult to use correctly (it's trivial to obtain aliasing exclusive references or encounter UB due to unsynchronised accesses to variables declared with `static mut`) and is becoming redundant due to the expansion of the interior mutability ecosystem which easily replace `static mut`.

# Guide-level explanation

`static mut` is meant to provide statics that can be modified after their initial value is set; variables declared with `static mut` can prove quite problematic when used, however:

```rust
static mut X: i32 = 0;

fn main() {
  let a = unsafe { &mut X };
  let b = unsafe { &mut X };

  println!("{a} {b}");
}

```

Recall Rust's borrowing rules:

- At any given time, you can have either one mutable reference or any number of immutable references.
- References must always be valid.

The first rule is violated as we have 2 exclusive (mutable) references to the same datum at the same time and are actively using them in an entirely overlapping fashion. This violation means that our code's behaviour is undefined, which means the optimiser is free to do with it as it wishes, potentially breaking it. The code is not guaranteed to print "0 0" and may fail to do so under some circumstances.

`static mut` also allows for unsyncronised access across multiple threads which can cause data races which are also undefined behaviour:

```rust
use std::thread::spawn;

static mut X: usize = 0;

const N: usize = 16;

fn main() {
    let mut thread_pool = Vec::with_capacity(N);
    
    for i in 0..N {
        thread_pool.push(spawn(move || {
            unsafe {
                X = i;
            }
            println!("{}", unsafe { FOO });
        }));
    }

    for thread in thread_pool {
        thread.join().unwrap();
    }
}

```

Here, since the usize is not an atomic (with predictable and defined relative ordering) nor synchronised with a `Mutex` or `RwLock` a data race takes place, printing numbers between 0 and 16 in a vaguely increasing fashion. This is undefined behaviour and means that our code is not correct. This and the previous example show UB that is almost trivial to cause which makes it prone to occur by accident in a large codebase.

Let's try to use `static mut` for FFI purposes (a common application of it), this is usually achieved in this fashion:

```rust
// using a symbol exported by C code
extern "C" { static mut _c_symbol: Ty; }

// exporting a symbol from rust code for use by C code
#[no_mangle]
pub static mut _rust_symbol: Ty = val;

```

This puts our code at risk of causing UB on access as we saw before. Accesses to `static mut` can become difficult to track and reason about very quickly as the size of the codebase increases. As such, by the 2024 edition, we get a deprecation warning (or even deny-by-default lint):

```rust
// WARNING: `static mut` syntax is deprecated as of edition 2024 and is slated
// for removal in edition 2027. Consider using std::cell::SyncUnsafeCell<T> instead. 
// Read more at (somewhere, maybe rust blog post).
// Note/fix: 
// - extern "C" { static mut _c_symbol: Ty; }
// + extern "C" { static _c_symbol: std::cell::SyncUnsafeCell<Ty>; }
extern "C" { static mut _c_symbol: Ty; }

// WARNING: `static mut` syntax is deprecated as of edition 2024, and is slated
// for removal in edition 2027. Consider using SyncUnsafeCell<T> instead. Read 
// more at (somewhere, maybe rust blog post).
// Note/fix: 
// - pub static mut _rust_symbol: Ty = val;
// + pub static _rust_symbol: std::cell::SyncUnsafeCell<Ty> = std::cell:SyncUnsafeCell::new(val);
#[no_mangle]
pub static mut _rust_symbol: Ty = val;

```

If we try to do the same thing in the 2027 edition we get a hard syntax error for not migrating:

```rust
// ERROR: expected identifier after `static` (or similar)
// Note:
// `static mut` syntax has been removed as of edition 2027, use std::cell::SyncUnsafeCell<T> instead.
// Fix:
// - extern "C" { static mut _c_symbol: Ty; }
// + extern "C" { static _c_symbol: std::cell::SyncUnsafeCell<Ty>; }
extern "C" { static mut _c_symbol: Ty; }

// ERROR: expected identifier after `static` (or similar)
// Note:
// `static mut` syntax has been removed as of edition 2027, use std::cell::SyncUnsafeCell<T> instead.
// Fix:
// - pub static mut _rust_symbol: Ty = val;
// + pub static _rust_symbol: std::cell::SyncUnsafeCell<Ty> = std::cell:SyncUnsafeCell::new(val);
#[no_mangle]
pub static mut _rust_symbol: Ty = val;

```

Migration from `static mut` in favor of `SyncUnsafeCell` makes code easier to audit, as some operations previously unsafe to do on `static mut` (such as obtaining a raw pointer to the static) become safe, shifting focus fully to the areas where problems might arise (where the raw pointers are dereferenced) as it is at those points where we create references from raw pointers or use the raw pointers to access the underlying data. Keep in mind, however, that while `SyncUnsafeCell `is a less obvious type/technique to find (harder for beginners to fall into using) and a more verbose one to use, it is still highly unsafe and still does allow someone determined to create aliasing exclusive references to a place; caution should be taken by users of `SyncUnsafeCell `and `UnsafeCell `in general.

If we follow the diagnostics given by the compiler, we can migrate our code to a safer version of itself and make it easier to audit for any mistakes by better isolating where they can occur. The use of intermediate raw pointers to obtain references also produces marginally better output from the [Miri tool](https://github.com/rust-lang/miri) which allows for better automated detection of problems in the code.

# Reference-level explanation

A lint can be declared for 2024 with a `FutureIncompatibilityReason` of `EditionError` triggered upon detection of a declaration in HIR or the AST. For 2027 a check can be added to the `Parser` struct in the `rustc_parse` crate.

There is little to no use of `static mut` in the compiler, it is mostly present in `std` and in the implementation of the runtime and in a fashion similar to the above code examples; the few points at which `static mut` is used can be migrated to `SyncUnsafeCell` without causing too much ado.

# Drawbacks

Verbosity increases slightly as values need to be wrapped in order to be placed in statics and methods need to be used to get at the underlying data.

# Rationale and alternatives

- Do nothing.
- Deprecate `static mut` but don't remove it.

Doing nothing would result in a redundant feature that serves no purpose other than being a potential trap for users. Deprecation without removal could be done, but after the presumed migration of at least a majority of the ecosystem after the deprecation lint there is no reason to keep the feature in the language.

# Prior art

## Work/discussion directly relevant

- Consider deprecation of UB-happy static mut at [#53649](https://github.com/rust-lang/rust/issues/53639)
- Disallow _references_ to `static mut` [Edition Idea] at [#114447](https://github.com/rust-lang/rust/issues/114447)
- Deprecate static mut (in the next edition?) on [IRLO](https://internals.rust-lang.org/t/deprecate-static-mut-in-the-next-edition/19975/12)

## Notable, not directly relevant

- SyncUnsafeCell at [#95439](https://github.com/rust-lang/rust/issues/95439)
- LazyCell/Lock at [#109736](https://github.com/rust-lang/rust/issues/109736)
- OnceCell/Lock at [#74465](https://github.com/rust-lang/rust/issues/74465)

Many have tried to remove/deprecate `static mut` before, the feature is now sufficiently redundant and subject to replacement to put forth a plan for its eventual removal.

# Unresolved questions

- Should the deprecation lint be warn-by-default or deny-by-default?
- Should we have `cargo fix` support for migration? It's technically possible, but reasonably high effort.

# Future possibilities

I can't think of any besides what was mentioned as of yet.

END RFC TEXT

Hi everyone, this is my first RFC written and my first post here on IRLO, so any feedback is welcome and highly appreciated! I've also created a [short fully anonymous survey](https://www.surveyhero.com/c/static-mut) to collect a large amount of data about this proposition, it's now on [This Week in Rust #527](https://this-week-in-rust.org/blog/2023/12/27/this-week-in-rust-527/).

---

<div class="post-metadata">

**Author:** ![Yoric](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/yoric/32/1316_2.png) [@Yoric](https://internals.rust-lang.org/u/Yoric)\
**Post date:** [December 23, 2023, 2:54pm UTC](https://internals.rust-lang.org/t/pre-rfc-deprecate-then-remove-static-mut/20072/2 "2023-12-23T14:54:19Z")

</div>

Are there guarantees that `std::cell::SyncUnsaceCell<Ty>` has the same representation as `Ty`?

---

<div class="post-metadata">

**Author:** ![Nemo157](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/nemo157/32/11585_2.png) [@Nemo157](https://internals.rust-lang.org/u/Nemo157)\
**Post date:** [December 23, 2023, 3:36pm UTC](https://internals.rust-lang.org/t/pre-rfc-deprecate-then-remove-static-mut/20072/3 "2023-12-23T15:36:49Z")

</div>

It's `#[repr(transparent)]` (other than presumably inheriting the niche suppression behavior of `UnsafeCell`, but that doesn't affect cases where it's the top-level type of a `static` allocation).

---

<div class="post-metadata">

**Author:** ![Jules-Bertholet](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/jules-bertholet/32/10671_2.png) [@Jules-Bertholet](https://internals.rust-lang.org/u/Jules-Bertholet)\
**Post date:** [December 23, 2023, 5:15pm UTC](https://internals.rust-lang.org/t/pre-rfc-deprecate-then-remove-static-mut/20072/4 "2023-12-23T17:15:15Z")

</div>

A `pub static mut` can't be migrated to `SyncUnsafeCell` without breaking public API. I think this would be the first instance where adopting a new edition requires making a semver-breaking change.

---

<div class="post-metadata">

**Author:** ![carbotaniuman](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/carbotaniuman/32/6981_2.png) [@carbotaniuman](https://internals.rust-lang.org/u/carbotaniuman)\
**Post date:** [December 23, 2023, 6:25pm UTC](https://internals.rust-lang.org/t/pre-rfc-deprecate-then-remove-static-mut/20072/5 "2023-12-23T18:25:55Z")

</div>

I don't think this is for any reason other than it hasn't come up yet. I can thing of `Range` changes and `set_env` also potentially requiring public API changes.

---

<div class="post-metadata">

**Author:** ![Yoric](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/yoric/32/1316_2.png) [@Yoric](https://internals.rust-lang.org/u/Yoric)\
**Post date:** [December 23, 2023, 8:56pm UTC](https://internals.rust-lang.org/t/pre-rfc-deprecate-then-remove-static-mut/20072/6 "2023-12-23T20:56:55Z")

</div>

Well, this could start with private / function-scoped `static mut`.

---

<div class="post-metadata">

**Author:** ![Nemo157](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/nemo157/32/11585_2.png) [@Nemo157](https://internals.rust-lang.org/u/Nemo157)\
**Post date:** [December 23, 2023, 9:33pm UTC](https://internals.rust-lang.org/t/pre-rfc-deprecate-then-remove-static-mut/20072/7 "2023-12-23T21:33:35Z")

</div>

> [@Jules-Bertholet](#):
>
> A `pub static mut` can't be migrated to `SyncUnsafeCell` without breaking public API.

It would be interesting to know if anyone has ever done that, that seems fraught with even more potential for footguns than crate-local `static mut`.

---

<div class="post-metadata">

**Author:** ![Jules-Bertholet](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/jules-bertholet/32/10671_2.png) [@Jules-Bertholet](https://internals.rust-lang.org/u/Jules-Bertholet)\
**Post date:** [December 23, 2023, 9:59pm UTC](https://internals.rust-lang.org/t/pre-rfc-deprecate-then-remove-static-mut/20072/8 "2023-12-23T21:59:06Z")

</div>

[https://sourcegraph.com/search?q=context:global+lang:Rust+pub+static+mut&patternType=standard&case=yes&sm=0&groupBy=repo](https://sourcegraph.com/search?q=context:global+lang:Rust+pub+static+mut&patternType=standard&case=yes&sm=0&groupBy=repo)

---

<div class="post-metadata">

**Author:** ![Nemo157](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/nemo157/32/11585_2.png) [@Nemo157](https://internals.rust-lang.org/u/Nemo157)\
**Post date:** [December 23, 2023, 10:05pm UTC](https://internals.rust-lang.org/t/pre-rfc-deprecate-then-remove-static-mut/20072/9 "2023-12-23T22:05:01Z")

</div>

That doesn't detect whether those exports are actually reachable from the crate root. It does look like there are some FFI bindings that are, but generally FFI binding crates should be single-private-use (especially if they have unguarded `static mut`) so breaking version bumps of them when they want to change edition shouldn't be an issue.

---

<div class="post-metadata">

**Author:** ![pitaj](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/pitaj/32/11262_2.png) [@pitaj](https://internals.rust-lang.org/u/pitaj)\
**Post date:** [December 24, 2023, 2:20am UTC](https://internals.rust-lang.org/t/pre-rfc-deprecate-then-remove-static-mut/20072/10 "2023-12-24T02:20:53Z")

</div>

Yeah this seems like a case where actual breakage is very unlikely, and any breakage is probably deserved.

---

<div class="post-metadata">

**Author:** ![dyslexicsteak](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/dyslexicsteak/32/11627_2.png) [@dyslexicsteak](https://internals.rust-lang.org/u/dyslexicsteak)\
**Post date:** [December 24, 2023, 9:37am UTC](https://internals.rust-lang.org/t/pre-rfc-deprecate-then-remove-static-mut/20072/11 "2023-12-24T09:37:44Z")

</div>

The other idea for disallowing references to `static mut` at [#114447](https://github.com/rust-lang/rust/issues/114447) is also semver-breaking.

---

<div class="post-metadata">

**Author:** ![jhpratt](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/jhpratt/32/11640_2.png) [@jhpratt](https://internals.rust-lang.org/u/jhpratt)\
**Post date:** [December 24, 2023, 11:32am UTC](https://internals.rust-lang.org/t/pre-rfc-deprecate-then-remove-static-mut/20072/12 "2023-12-24T11:32:10Z")

</div>

That wouldn't affect public API, though.

---

<div class="post-metadata">

**Author:** ![dyslexicsteak](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/dyslexicsteak/32/11627_2.png) [@dyslexicsteak](https://internals.rust-lang.org/u/dyslexicsteak)\
**Post date:** [December 24, 2023, 12:49pm UTC](https://internals.rust-lang.org/t/pre-rfc-deprecate-then-remove-static-mut/20072/13 "2023-12-24T12:49:56Z")

</div>

Why? I heard that if a large portion of usages of `static mut` broke then it's considered a breakage of semver even if there was no way to fix it by changing your API.

---

<div class="post-metadata">

**Author:** ![carbotaniuman](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/carbotaniuman/32/6981_2.png) [@carbotaniuman](https://internals.rust-lang.org/u/carbotaniuman)\
**Post date:** [December 24, 2023, 4:33pm UTC](https://internals.rust-lang.org/t/pre-rfc-deprecate-then-remove-static-mut/20072/14 "2023-12-24T16:33:26Z")

</div>

Each use case can switch to taking a raw pointer and then turning that into a reference.

---

<div class="post-metadata">

**Author:** ![kpreid](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/kpreid/32/8484_2.png) [@kpreid](https://internals.rust-lang.org/u/kpreid)\
**Post date:** [December 24, 2023, 5:07pm UTC](https://internals.rust-lang.org/t/pre-rfc-deprecate-then-remove-static-mut/20072/15 "2023-12-24T17:07:46Z")

</div>

> [@Jules-Bertholet](#):
>
> A `pub static mut` can't be migrated to `SyncUnsafeCell` without breaking public API.

It would be possible to deprecate `static mut` without actually removing it. There is precedent to do this to address safety-usability mistakes; [`mem::uninitialized()`](https://doc.rust-lang.org/std/mem/fn.uninitialized.html) is deprecated but not scheduled for removal.

---

<div class="post-metadata">

**Author:** ![dyslexicsteak](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/dyslexicsteak/32/11627_2.png) [@dyslexicsteak](https://internals.rust-lang.org/u/dyslexicsteak)\
**Post date:** [December 24, 2023, 5:18pm UTC](https://internals.rust-lang.org/t/pre-rfc-deprecate-then-remove-static-mut/20072/16 "2023-12-24T17:18:27Z")

</div>

Yes, this could even be achieved by `cargo fix` though I'm not sure if we really want to do that or if it's assuming too much in a way.

---

<div class="post-metadata">

**Author:** ![CAD97](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cad97/32/3460_2.png) [@CAD97](https://internals.rust-lang.org/u/CAD97)\
**Post date:** [December 24, 2023, 5:47pm UTC](https://internals.rust-lang.org/t/pre-rfc-deprecate-then-remove-static-mut/20072/17 "2023-12-24T17:47:46Z")

</div>

> [@kpreid](#):
>
> [`mem::uninitialized()`](https://doc.rust-lang.org/std/mem/fn.uninitialized.html) is deprecated but not scheduled for removal.

...but it's brought up for discussion whether we can / should be hiding it from the new edition every cycle. But also `mem::uninitialized` is also quite special in being essentially unfit for any use\[1\]. `static mut: T` is a giant footgun almost always better expressed as `static: SyncUnsafeCell<T>`\[2\], but it's at least fit for its advertised purpose.

* * *

1. And essentially any use where it isn't immediate UB being an alias for `MaybeUninit::uninit()` or `MaybeUninit::uninit_array()`. 

2. FWIW, I don't think it's practical to deprecate `static mut` until `SyncUnsafeCell` is available in stable std, as that's the direct replacement, since `UnsafeCell` can't be used in `static` without a `Sync` wrapper of some kind.

---

<div class="post-metadata">

**Author:** ![dyslexicsteak](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/dyslexicsteak/32/11627_2.png) [@dyslexicsteak](https://internals.rust-lang.org/u/dyslexicsteak)\
**Post date:** [December 24, 2023, 5:50pm UTC](https://internals.rust-lang.org/t/pre-rfc-deprecate-then-remove-static-mut/20072/18 "2023-12-24T17:50:29Z")

</div>

To be clear, we only want to do this if `SyncUnsafeCell` is also coming along for 2024.

---

<div class="post-metadata">

**Author:** ![Sahl](https://avatars.discourse-cdn.com/v4/letter/s/b3f665/32.png) [@Sahl](https://internals.rust-lang.org/u/Sahl)\
**Post date:** [December 24, 2023, 5:54pm UTC](https://internals.rust-lang.org/t/pre-rfc-deprecate-then-remove-static-mut/20072/19 "2023-12-24T17:54:40Z")

</div>

I think there is a way to remove `pub static mut` across an edition boundary without breaking backwards compatibility.

Imagine you have an existing pub static mut in your crate:

```rust
pub static mut STATIC_MUT: u32 = 123;

```

This is used by downstream crates, using any edition in {2015, 2018, 2021}. In their code, usage looks like this

```rust
let r = &mut STATIC_MUT;
// or
let r = &STATIC_MUT;

```

With my proposal, you can change your crate in the following way:

```rust
pub static STATIC_MUT: SyncUnsafeCell<u32> = SyncUnsafeCell::new(123);

```

Now the downstream crates look the same, but you backport some new syntax:

```rust
let r = &mut STATIC_MUT
// desugars to 
let r = STATIC_MUT.get_mut() as &mut _;

let r = &STATIC_MUT;
// desugars to
let r = &STATIC_MUT.get() as &_;

```

This new syntax will exist for the {2015,2018,2021} editions but not for the 2024 edition. Additionally, this is very easy to apply in an automated fashion al a `cargo fix`.

There's precedent for this kind of syntax backfill with [IntoIter for arrays](https://doc.rust-lang.org/edition-guide/rust-2021/IntoIterator-for-arrays.html).

### Details:

Assuming `pub static mut` is pretty rare, this sugar won't come up often, likely significantly less often than the array `into_iter` example.

If you still want to get a `&SyncUnsafeCell<u32>` on those earlier editions, we add a method:

```rust
let r: &SyncUnsafeCell<u32> = SyncUnsafeCell::ref(STATIC_MUT)

```

This method will be a linter error on the 2024 edition, and `cargo fix` should replace it with a simple &.

We can (and should) also skip the sugar for the defining crate, since you're changing the static yourself you can change the rest of your code to use `SyncUnsafeCell`'s accessor methods.

---

<div class="post-metadata">

**Author:** ![CAD97](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/cad97/32/3460_2.png) [@CAD97](https://internals.rust-lang.org/u/CAD97)\
**Post date:** [December 24, 2023, 6:53pm UTC](https://internals.rust-lang.org/t/pre-rfc-deprecate-then-remove-static-mut/20072/20 "2023-12-24T18:53:23Z")

</div>

> [@Sahl](#):
>
> There's precedent for this kind of syntax backfill with [IntoIter for arrays](https://doc.rust-lang.org/edition-guide/rust-2021/IntoIterator-for-arrays.html).

Not really? The edition difference is exactly and only that `<[T; N] as IntoIterator>::into_iter` is hidden from method lookup in edition 2015. There's no extra special desugaring going on there.

The closest equivalent for `static mut` wouldn't be special sugar to make `&`[`mut `]`sync_unsafe_cell` do `&`[`mut `]`*sync_unsafe_cell.get()`, it'd be magic to make old editions see `static: SyncUnsafeCell<T>` as if it's defined with `static mut: T` instead.

It's honestly unnecessary to _remove_ `static mut` from new editions. If it's safe to `addr_of`[`_mut`]`!` it and `unsafe` to `&`[`mut`] it (i.e. how we treat `#[packed]` fields with non-trivial alignment requirements), that puts a sufficient safety lock on the footgun and makes it effectively equivalent to using `SyncUnsafeCell`.

[Next page](https://internals.rust-lang.org/t/pre-rfc-deprecate-then-remove-static-mut/20072.md?page=2)
