- Ada? Many cool features, but that's also the problem - it's damn complicated.
- C++ combines simplicity of Ada with safety of C.
- Rust and Zig are only ~10 years old and haven't really stabilized yet. They also start to suffer from npm-like syndrome, which is much more problematic for a systems language.
- ATS? F#? Not all low-level stuff needs (or can afford) this level of assurance.
- Idris? Much nicer than ATS, but it's even younger than Rust and still a running target (and I'm not sure if zero-runtime support is there yet).
I mean, yes, C is missing tons of potentially useful features (syntactic macros, for one thing), but closures are not one of them.
I'm trying to drag one program at $employer up to C99 (plus C11 _Generic), so I can then subsequently drag it to the bits of C23 which GCC 13 supports.
This all takes times, and having to convince colleagues during code reviews.
What C23 has done is authorise some of the extensions which GCC has had for some time as legitimate things (typeof, etc).
However the ability to adopt is also limited by what third party linters in use at $employer may also support.
Compare e. g. https://github.com/rust-lang/rust/pull/102750/ (I'm not following Rust development closely, picked up the first one). Yes, developers do rely on non-guaranteed behavior, that's their nature. C would likely standardize old behavior. Basically all of the C standard is a promoted vendor-specific extension - for better or worse.
Here is another good one: https://internals.rust-lang.org/t/type-inference-breakage-in...
C23 on the other hand landed a bunch of hard changes, and "it didn't break my code" only matches the reality most people observe with Rust too. The changes you mention didn't break my Rust either.
But some trivial C did break because C23 is a new language
https://c.godbolt.org/z/n9vhMGYW5
In fact those Rust changes aren't language changes unlike C23, the first you linked is a choice for the compiler to improve on layout it didn't guarantee, anybody who was relying on that layout (and there were a few) was never promised this wouldn't change, next version, next week or indeed the next time they compiled the same code. You can even ask Rust's compiler to help light a fire under you on this by arbitrarily changing some layout between builds, so that stuff which depends on unpromised layout breaks earlier, reminding you to actually tell Rust e.g. repr(C) "I need the same layout guarantees for my type T as I'd get in C" or repr(transparent) "I need to ensure my type wrapper T has the same layout as the type I'm wrapping"
The second isn't a language change at all, it's a change to a library feature, so now we're down to "C isn't stable until the C library is forever unchanging" which hopefully you recognise as entirely silly. Needless to say nobody does that.
Even if not perfect, "Typescript for C" came out in 1983.
Which functions are required for a C program to run aside from those that it run explicitly? memcpy? It's basically a missing CPU instruction. malloc will never get called unless you call it yourself.
> followed by history of systems programming languages, starting with JOVIAL in 1958.
All system languages (e. g. BLISS, PL/S, NEWT) designed as such before C was vendor-specific. Some of these had nice things missing from C, but none propagated beyond their original platform. And today we have no option but C.
> "Typescript for C" came out in 1983.
C++ is not just "not perfect", it is far worse in every way. Let's let people overload everything. And let's overload shift operator to do IO. And make every error message no less than 1000 lines, with actual error somewhere in the middle. Let's break union and struct type punning because screw you that's why. You say C macros are unreadable? Behold, template metaprogramming! C is not perfect, but it has the justification of being minimal. C++ is a huge garbage dumpster on fire.
C was also vendor specific to Bell Labs.
C++ is definitely Typescript for C.