They are to be used with caution. If your execution environment is simple enough they can be quite useful and effective. Engineering shouldn't be a religion.
> I can not count how many times I have been pulled in to debug some gnarly crash and the result was, inevitably, a mutable global variable.
I've never once had that happen. What types of code are you working on that this occurs so frequently?
Saud by many an engineer whose code was running in systems that were in fact not that simple!
What is irksome is that globals are actually just kinda straight worse. Like the code that doesn't use a singleton and simply passes a god damn pointer turns out to be the simpler and easier thing to do.
> What types of code are you working on that this occurs so frequently?
Assorted C++ projects.
It is particularly irksome when libraries have globals. No. Just no never. Libraries should always have functions for "CreateContext" and "DestroyContext". And the public API should take a context handle.
Design your library right from the start. Because you don't know what execution environments will run in. And it's a hell of a lot easier to do it right from the start than to try and undo your evilness down the road.
All I want in life is a pure C API. It is simple and elegant and delightful and you can wrap it to run in any programming environment in existence.
Sure thing boss, here's that header file populated exclusively by preprocessor macros that you asked for.
Anyways, I think there are probably better solutions to the problem than globals, we just haven't seen a language quite solve it yet.
The way Blizzard implemented this is super super clever. They created an entirely duplicate "replay world". When you die the server very quickly "backfills" data in the "replay world". (Server doesn't send all data initially to help prevent cheating). The camera then flips to render the "replay world" while the "gameplay world" continues to receives updates. After a few seconds the camera flips back to the "gameplay world" which is still up-to-date and ready to rock.
Implementing this feature required getting rid of all their evil dirty global variables. Because pretty much every time someone asserted "oh we'll only ever have one of these!" that turned out to be wrong. This is a big part of the talk. Mutables globals are bad!
> Extra large codebases have controllers/managers that must be accessible by many modules.
I would say in almost every single case the code is better and cleaner to not use mutable globals. I might make a begrudging exception for logging. But very begrudgingly. Go/Zig/Rust/C/C++ don't have a good logging solution. Jai has an implict context pointer which is clever and interesting.
Rust uses the unsafe keyword as an "escape hatch". If I wrote a programming language I probably would, begrudgingly, allow mutable globals. But I would hide their declaration and usage behind the keyworld `unsafe_and_evil`. Such that every single time a programmer either declared or accessed a mutable global they would have to type out `unsafe_and_evil` and acknowledge their misdeeds.
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.
4 of have to think about.
And yes 5 is the evilness.
There's lots of interest things you could do with a rust like (in terms of correctness properties) high level language, and getting rid of global variables might be one of them (though I can see arguments in both directions). Hopefully someone makes a good one some day.
doesn't imply you have to expose it as a global mutable variable