Another would be static mutable variables, which are perfectly safe in a single-threaded environment, doubly so if you map that memory on a per-core basis to ensure each kernel has a unique variable value. Esp if you need lazy_static then there aren't many options other than wrapping the data in a mutex for no good reason.
`static mut` is not always safe even in a single-threaded environment, because of reentrance - see also https://github.com/rust-lang/rust/issues/53639.
Reentrance is not the issue here, I guard this inside the data structure, interrupts will be worst case reentrance most of the time.
But without guarding mutability behind some way to indicate interrupts are disabled, or an outright lock, and without guaranteeing no data races (Sync), it's not actually safe.
And the compiler can't make `static mut` safe to use as any of those requirements could be broken and then it's not safe at all anymore.
While the Linux kernel can do this, it's not easy to write from scratch, especially because Linux is in C so the compiler doesn't complain about weird things you do.
I'm very very aware of that though this is not always true.
For example Rust doesn't have a sane way to have a reentrant mutex without including std, which is bad because for interrupts in a kernel you need to either have very fine grained locking and fallbacks or rely entirely on lockless operations. Interrupts can occur on top of eachother in which case it behave a lot worse than simply being reentrant.
rustc yells at me a lot for the solutions this requires because the way I solve it violates ownership rules of rust the hard way to make the code simpler and understandable.
Other times it's when for bizarre reason rust wants the code to implement the Sync and Send traits despite the kernel running alone on the core (there is no threading in that kernel) and I would love to be able to tell rustc to just shut up about this and simply ignore Send, possibly even Sync, on a module level.