by johnnyjeans | 2025-05-16 17:17:14+00:00
> 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.