Rust is the only real competitor, but given the sheer amount of existing C and C++ code, the idea that they will become legacy languages any time soon is pretty out to lunch.
A lot of command line tooling is being written in Go along with systems software (like basically all of container land). Maybe some of these would have otherwise been written in C or C++. For example, if you use the Azure storage GUI to manage storage the underlying "thing" that copies data from your filesystem to azure storage is written in Go and that's why it works on Windows, Linux, MacOS. C++ is somewhat more portable than C so maybe something like that would have been written in C++? Who knows? But Go is definitely showing up in all sorts of native, command-line stuff. And Rob Pike - one of the people who developed Go - intended it to be a new home for C++ developers as well.
Rust is definitely difficult to learn, but I'll anecdotally say that Rust helped get me into the embedded world as someone with very little C or C++ experience. Unlike Ada, Rust has a modern toolchain and type system that feels very comfortable to developers coming from higher level languages, even if many of the concepts related to manual memory management remain quite difficult.
We have a modern toolchain too :)
# install compiler and build tools
alr toolchain --select
# make, build and run a project
alr init --bin my_project
alr build
alr runThey might both have a rule like: AV Rule 1 Any one function (or method) will contain no more than 200 logical source lines of code (L- SLOCs).
But Ada would probably not have something like: AV Rule 59 (MISRA Rule 59, Revised) The statements forming the body of an if, else if, else, while, do...while or for statement shall always be enclosed in braces, even if the braces form an empty block.
Or: AV Rule 193 (MISRA Rule 61) Every non-empty case clause in a switch statement shall be terminated with a break statement.
Those two arise from deficiencies in the basic C/C++ syntax. They are probably not fixable in a future revision of the spec because they would break too much code. So we create a coding standard, configure a linter to check for it, wire up our source repository to scan for it on checkin, and make all the leads code review for it.
Ada might have different language specific standards. For example, around not allowing breaks out of infinite loops. Requiring, instead, a loop construct with a testable condition. But even then, you can pragma things out of the compiler to help you enforce behavior. That's "built in."
My comment with Rust is not that Ada is bad or too hard, but rather even a small amount of additional work might be enough for developers and organizations to avoid Rust. There's a lot of Rust advocacy, which is a good thing. However, there was also a lot of activity on comp.lang.ada.advocacy (if I remember the newsgroup correctly). One good thing is that Ada felt "imposed" by a number of people and that automatically stimulates a kind of rejection response. Rust's introduction is definitely more "bottom up."
It isn't that hard to define something like MyType_t which is a pointer. Then some C code to allocate to wrap the MyType so the type is usable from any language.
I like to live in the embedded world and there I think Rust will shortly start to push out C and C++ at the wide-spread, commercial level. This is where my head is at, maybe it helps you formulate your opinion:
First, embedded C++ is not the same a full C++ (with features or library parts missing to accommodate the constrained environment). So I might never see some of the C++2x or even C++1x features. I really don't care about the C++2x standards since I might not see them in the next 10 years.
Second, memory and concurrency issues are real, even when you don't allow dynamic memory allocation after the system is initialized. We test, then test, and then run more tests, and turn on a lot of linters and analyzers but at the end of the day, they're bandaids. You can write terrible code in Rust, but you have to work harder to do it.
Third, if you really need an environment where memory is as fungible as play dough but want something portable (like inside a kernel), there will always be C. Setting up a C callable environment from assembly is well understood. In theory, calling into Rust shouldn't be any harder. But reading disassembled object files from C is generally reasonable assembler for the C I wrote. I haven't really tried doing that with Rust.
Fourth, it will take 5 years or so, but eventually we'll see commercially supported RTOS implementations for Rust that help companies to shield themselves from liability. For example, something like Threadx (AzureRTOS now). This will help push money in a virtuous cycle of better tooling -> more projects -> more money for tool venders -> better tooling.
Fifth, there will be Rust bindings to cover just about everything you do with C++ now (like game development) with high quality, idiot-proof, and portable semantics. You will find that junior developers or fresh-outs will have their skin crawl when asked to write "unsafe" code.
Sixth, there's counter evidence to support a lack of adoption of new languages. Ada provides many of the same concurrency and memory safety guarantees as Rust, and has been available since 1983, but it is not widely adopted. It is harder to use and requires significant developer training. If Rust is significantly more challenging for developers, it may go the way of Ada.
But that's in the future. If I had to start a new project today (for a braking system on a train, for example), I would start with C, a validated RTOS, and commercial toolchain. If I had to light up my kid's halloween costume? Embedded Rust as a learning exercise. The rendering engine for a VR headset (with limited batter and compute), probably C++.