Have the Rust and Zig communities been outright lying to me? I know very little about the details. I genuinely thought that the overhead of GC was a well-established tradeoff in language design.
That some specific kinds of GCs, specifically moving GCs (of the kind used in Java and .NET, but not the kinds used in Python or Go, which are also very different from each other) can be highly efficient, at least in principle, has been well known since the eighties (see Garbage Collection Can Be Faster Than Stack Allocation, 1986 [1]). However:
1. Moving collectors have long been more efficient than malloc/free but only in the total work devoted to heap management, i.e. throughput. Moving collectors that also offer predictable low latency are much more recent. In fact, the first open-source, production-quality, high-throughput, low-latency moving collector is less than three years old [2].
2. Much more importantly - and this is something that we experienced low-level programmers have known forever but people without much experience in low-level programming seem to not know these days is that low-level languages are not optimised for maximum performance. They're designed for maximum low-level control. When programs are small, when they're not heavily concurrent, or when the hot path is relatively simple, this low-level control can, indeed, translate to very high performance. But when programs grow larger or more concurrent, low-level control can actually make some optimisations harder. In particular, moving pointers, which are required to enjoy the optimisation offered by moving collectors, is not compatible with the low-level control needed in low-level languages, and these languages prioritise low-level control over everything else, including performance (as low-level control is their primary purpose). And this is not the only example of optimisations that these languages make harder. This is why large and/or concurrent programs have largely migrated from C++ to Java and C# over the past few decades, and this trend isn't reversing. Again, for smaller and/or less concurrent programs, low-level languages still offer excellent performance in expert hands, provided you invest sufficient effort into manual optimisation.
3. The optimisation offered by moving collectors isn't free. Until recently, you had to pay in unpredictability and high latency (which is still the case in all languages except Java), and since the algorithm uses RAM to reduce the CPU needed for heap management, it does necessarily require higher footprint. You can enjoy a similar optimisation that turns RAM into free CPU in Zig by using arenas (this is harder to do in C++ and Rust). Zig's arenas are even more efficient than moving collectors, but they do require more effort, and they're less general.
[1]: https://www.cs.princeton.edu/techreports/1986/045.pdf Note that in practice, moving GCs are not quite as efficient as stack allocation (let alone more efficient), but heap allocations in Java are not as expensive as heap allocations in C/C++/Rust/Zig that utilise malloc/free. This is why in these languages we try to avoid heap allocations on the fast path.