I think unless your code is guaranteed to never interact with any untrusted input it is nowadays an increasingly unacceptable compromise to just accept that your program might have serious flaws which can lead to remote code execution or worse.
Moreover, it becomes increasingly unpleasant and unworkable to deal with code which progressively gets more and more unreliable.
It's expected that if the complexity of a program grows, the state space that the program can occupy grows with it. But with UB you can run into by accident that state space seems to grow exponentially in comparison to a language like Rust.
If you are required to write code at that low level, I would not use anything other than something like rust.
If you are not required to write code at that level. There are many languages with much less uncertainty than C++ which are much more productive than either C++ or rust.
> I think unless your code is guaranteed to never interact with any untrusted input it is nowadays an increasingly unacceptable compromise to just accept that your program might have serious flaws which can lead to remote code execution or worse.
I think that's too strong a statement, because it applies to in-development programs. I agree with you if you're talking about released programs, but there can be benefit in leaving open the possibility of detectable flaws, serious or otherwise, while your code is still in development.
It's analogous to only compiling and running in debug mode throughout your development, and then switching to release mode for the final binary. The binary is suboptimal throughout your development process; it's too slow. But as long as the `--release` flag doesn't require any code changes, it's still a better idea than developing entirely in release mode.
Similarly, the binary could be suboptimal from a correctness standpoint, as long as removing the `--devel` flag only works when the compiler is fully happy. `--devel` could turn some borrow checking failures into warnings and still give you a runnable binary. Or it could allow leaving types underspecified in interfaces, and do an unsound type inference. Best case, it could even do runtime checks and/or coercions to establish the assumptions that the callee was compiled with.
Whether it would be worth the complexity is an open question, but it seems reasonably clear that Rust has a problem with brittleness to development-time change.
If you develop C or C++ haphazardly in such a way that you leave a bunch of UB on the table during development then there's little to no chance that you'll have actually erased all presence of it by the end of development.
There currently exist no tools which with complete reliability point out all UB in your program. If any part of your program can have UB and you didn't write it with the explicit intention of not having UB in it at any point then you're going to be left with a tough situation to deal with.
I've read a lot of C in my time and there's codebases which I read and find easy and quick to review because they stick to the rules and only bend them sparingly and then there's codebases which are a pain to evaluate even the most basic parts for errors and UB.
There's no such thing as "detectable UB", there's only UB which your tools have luckily managed to detect.
Leave the UB to the people who can't avoid it, stick to safe languages when you can.
Moreover, it becomes increasingly unpleasant and unworkable to deal with code which progressively gets more and more unreliable.
It's expected that if the complexity of a program grows, the state space that the program can occupy grows with it. But with UB you can run into by accident that state space seems to grow exponentially in comparison to a language like Rust.
If you are required to write code at that low level, I would not use anything other than something like rust.
If you are not required to write code at that level. There are many languages with much less uncertainty than C++ which are much more productive than either C++ or rust.