If we're getting philosophical, we can identify a hierarchy of globals:
1. Read-only (`const`s in Rust). These are fine, no objections.
2. Automatic-lazily-initialized write-once, read-only thereafter (`LazyLock` in Rust). These are also basically fine.
3. Manually-initialized write-once, read-only thereafter (`OnceLock` in Rust). These are also basically fine, but slightly more annoying because you need to be sure to manually cover all possible initialization pathways.
4. Write-only. This is where loggers are, and these are also basically fine.
5. Arbitrary read/write. This is the root of all evil, and what we classically mean when we say "global mutable state".
2 and 3 are basically fine. Just so long as you don’t rely on initialization order. And don’t have meaningful cleanup. C++ initialization fiasco is great pain. Crash on shutdown bugs are soooo common with globals.
1. Read-only (`const`s in Rust). These are fine, no objections.
2. Automatic-lazily-initialized write-once, read-only thereafter (`LazyLock` in Rust). These are also basically fine.
3. Manually-initialized write-once, read-only thereafter (`OnceLock` in Rust). These are also basically fine, but slightly more annoying because you need to be sure to manually cover all possible initialization pathways.
4. Write-only. This is where loggers are, and these are also basically fine.
5. Arbitrary read/write. This is the root of all evil, and what we classically mean when we say "global mutable state".