For better or worse, modern C++ is still the most capable and expressive systems language. To replace it, we need (at a minimum) a language with similar capability and expressiveness in the low-level systems domain. The options are really thin; Zig probably comes the closest but it is a bit austere coming from recent C++ versions.
I think we can do significantly better than C++ as a systems language. We just haven’t landed on a language that really nails the design.
> For better or worse, modern C++ is still the most capable and expressive systems language.
Not really. Rust, ATS, D, and some implementations of Lisp and even Haskell if you slay the dragon of actually learning GHC. Modern C++ is honestly overrated in my opinion as an extensive user of the language with 300k lines in a modern dialect alone. It's a pain in the ass to step through in a visual debugger and core dumps may as well be useless no matter the platform. It's extremely grating to read and to write. Compilers have a tendency to crash like crazy if you get too cute. There's still no compile-time (or runtime) reflection. I would literally rather be writing proofs for a dependent type system than deal with heavy template metaprogramming.
C++20 metaprogramming is pretty clean and straightforward. It became usable around C++17, though the learning curve is a bit steep. I can’t remember the last time I saw a compiler crash, and I’ve used many compilers on code bases that use a lot of dark corners of C++. The occasional compiler bug is a thing though.
I didn’t say C++ was amazing, just that recent dialects are better than the alternatives in practice, many of which I have also used for similar code.
Rust is not a substitute for C++ unless your code isn’t all that low-level, it lives closer to the abstraction level of Java. There are a number of odd gaps in the Rust feature set for low-level systems programming (database kernels in my case), the rigid ownership/lifetime model doesn’t play nicely with fairly standard systems-y things like DMA, and the code is always runs slower for some inexplicable reason.
I’d love something to replace C++ but to be a candidate it can’t be less expressive, slower, and so rigid that it is difficult to do some ordinary systems-y things. A nerfed programming language is not the answer to this question.
> C++20 metaprogramming is pretty clean and straightforward.
I disagree. Having to juggle two type systems and two languages simultaneously is inherently unclean, to say nothing of how noisy and unergonomic templates end up being. For anything non-trivial they're a mess. Imagine I handed you a codebase with a few hundred templated structures, averaging about 50 parameters each, many of which are variadic, many of which are nesting as parameters within each other. As you climb through this pile, you end up in a totally different layer, where these more complicated templated structures are passing around and working on a much larger collection of simpler templated structures and constexpr. You're not going to have a fun time, no matter how comfortable you are with templates.
> I can’t remember the last time I saw a compiler crash
How long of a parameter pack do you think it will take to cause a crash? Of course eventually the compiler has no more memory to spare, and that'll be a crash. Clang will helpfully complain:
warning: stack nearly exhausted; compilation time may suffer, and crashes due to stack overflow are likely
But I assure you, the compiler will crash long before it runs out of memory. If you're in the domain of non-trivial meta-templates, these packs can explode to ludicrous sizes from recursive concatenation, and especially if you have a logic error somewhere that generates and concatenates packs you didn't want. And that's just the obviously intuitive example. Now contextualize this in the codebase I describe above.The more you push templates as a turing-complete language to their maximum potential, the more compiler issues you run into. Templates are probably the most rigid and unstable metaprogramming facility I've experienced, honestly. GCC and cl.exe are the worst for it.
> it lives closer to the abstraction level of Java.
That's interesting, because Java isn't very abstracted over the JVM. It's that extra layer of the JVM being an abstraction over another CPU architecture that makes Java abstract. C and C++ are arguably more abstracted, because they have to be. But just like Rust, they provide good general semantics for any register machine that the abstractions can mostly be compiled away.
> the rigid ownership/lifetime model doesn’t play nicely with fairly standard systems-y things like DMA
Choosing references as your default is insane, honestly. Safe Rust is akin to writing formally verified software in the mental and ergonomic overhead it incurs. That's not coincidental. I've come to the conclusion one of the biggest mistakes of the community is not pushing pointer-only Rust harder, given they want it to be taken seriously as a systems language.
Rust's safety is irrelevant as far as I'm concerned. It's nice that it has it, but I (and everybody else working in C++) is used to working without it and don't really miss it when it's gone.
> and the code is always runs slower for some inexplicable reason.
As you come to understand Rust as a set of assembler macros and rewrite rules applied over them, I've found they're neither more or less granular than the C/C++ ones. They're just a different family, and it stems from a different style of assembly programming (of which there are many). If you've ever translated a lisp with manual memory management onto a register machine, it's similar to that way of thinking about how to compose LOAD/STORE/JUMP + arithmetic, which it got from the ISWIM/ML heritage. Just like with C/C++, everything else is syntax sugar and metaprogramming. At the core of it, you should still be able to translate your Rust code into your target's machine code in your head relatively trivially.
> Choosing references as your default is insane, honestly. Safe Rust is akin to writing formally verified software in the mental and ergonomic overhead it incurs. That's not coincidental. I've come to the conclusion one of the biggest mistakes of the community is not pushing pointer-only Rust harder, given they want it to be taken seriously as a systems language.
That's an interesting perspective I hadn't heard before, but I think there are a couple problems. One, the culture is really against using unsafe unless it's impossible to write the code using safe Rust. This happened with the Actix web drama in 2020 [1]. And, there is the opinion that unsafe Rust is harder than C [2]. Not just that, but unsafe Rust is really ugly and annoying to use compared to safe Rust. I think that hinders the potential adoption of your idea, too.
[1]: https://steveklabnik.com/writing/a-sad-day-for-rust/
[2]: https://chadaustin.me/2024/10/intrusive-linked-list-in-rust/