>There's really no proof that systems written in Rust have better reliability compared to systems written in Java, Typescript or PHP, is there?
I am not here to prove mathematically that Rust results in more correct programs than PHP. I am here to claim that I believe that it does.
To me one of the biggest obstacles to writing correct code is not just memory safety. It's preventing unintended uses of code. It's handling every case.
PHP is a memory safe language, but that does not mean it is equally good for writing complex software. Why? Because:
- Exception and error handling in PHP, including the standard library, is inconsistent and tricky to get right. In Go and Rust idiomatic error handling is pretty easy to get actually correct.
- Code is not only not type safe, but you might not even notice when you violate type safety. Many things that are runtime errors in Python and compile time errors in Rust or Go are incorrect behavior in PHP.
- The TypeScript, Rust, and Go compilers and toolchains are more rigid, because more strict languages make that possible. For example, if you fail to return in every possible code path of a function, that's an error. In TypeScript, any unchecked possibly null access is an error when using strict nulls. Many more examples exist.
>Finally, comparing C++ and Rust reliability is much more nuanced than your dismissive attempt. Modern C++ written by an experienced team + good tooling can have really good memory safety, not as good as Rust, but good enough so that the other advantages of C++ can tip the balance in its favor.
OK, it feels like you're just being contrarian now. I've made this same argument before as a reason why we shouldn't all just rewrite everything in Rust.
I've worked on a fair bit of large C++ projects before. My current employer, Google, does quite a great job with the C++ toolchain, and admittedly while I do not have as much experience with it as I do C++ outside of Google, I'd say it's close to the best I've ever seen.
And even then... It's ass ugly, the errors are confusing as hell, I can't understand what the hell is going on in some of the macros, and it's still possible, with metric tons of safeguards, to fuck up and segfault (guilty.)
Something C++ fans (including me) love to point out is that if you enforce the use of smart pointers you effectively can prevent whole classes of memory issues. In reality I'm pretty sure everyone knows that's a bit of an exaggeration. It's still pretty easy to misuse a smart pointer. And on top of that, smart pointer syntax can be confusing. I used to be a huge proponent of operator overloading but after not having it for a while I'm pretty sure it has a tendency to turn verbose code into confusing but terse code.
C++ just has too much fucking syntax. And it has templates. And on top of that syntax it has text macros. And even if you ban new macros, you still need macros for very basic things like "logging with line numbers". And it's useful for things where none of the other available tools solve the problem. So good C++ code probably ends up using a lot of macros even if most of it is not defining new ones.
People have made C++ fault tolerant as much as possible because they have to. There's too much C++ in the world to stop and rewrite it all in some memory safe language. No amount of language debates on Hacker News will ever change that.
But, we should not let that cloud the fact that better C++ doesn't come close to providing strong safety guarantees, and it sure doesn't prevent obscene looking code. As much as people will hate Gofmt, I've never seen uglier code than some of the edge cases as handled by clang-format. (If you know of a C++ formatting tool that's any better, please tell me.)
So does Rust result in more correct code? Does Go? I believe so yes. For starters, C++ error handling is atrocious. Some people use exceptions, some people don't. Google uses StatusOr[1] and I think the best case would be something like Andrei Alexandrescu's expected<t>. In reality you get a mix of custom status codes, C style error handling, weird OOP style error handling where objects contain error statuses (especially when you can't throw in a constructor.) Even at Google there are third party libraries, with both C and C++ APIs, so there's still some mismatch that has to be dealt with. The standard library is just as guilty here.
How about safety? Even with improvements it's really hard to make a subset of C++ where you can't accidentally segfault. Sometimes, you have a smart pointer and you need a regular pointer. So what do you do, dereference? Well sure... But now what are you thinking about? Lifetimes. And thus, the value proposition of Rust. Go solves the lifetime problem with garbage collection, but in C++ you just have to manually, as a human, figure out what lifetimes are expected, and in the meantime someone could submit code that subtly changes this and when you submit your code now head is broken even though neither person made a mistake.
The list really could go on for a long time. And I expect every point here to be disputed because there's some way to mitigate the problem. Reflect for a moment, though, that all the energy spent trying to mitigate problems like these could've been spent on writing better code elsewhere if it weren't for the shortcomings of the language.
1: http://www.furidamu.org/blog/2017/01/28/error-handling-with-...