Rust gets safety with its ownership system. The ownership system brings strong guarantees around who is mutating / reading any given object.
Python has uncontrolled ownership. Any function can generally mutate / store any variable you pass in. This can definitely cause bugs if a rouge function starts modifying input.
Rust makes this impossible without the mutation “permission” being part of the method signature. Strong ownership controls force functions to be extremely explicit with their intentions
Rust keeps track of this, no matter how deep you twist your logic and it does so for every variable, no context manager blocks required.
it LOOKS like it's possible but it can be broken easily. again, because rust is not really about (i)mutability, it's about mem-safety - and all of those can be safely mutated with &T
Rust has a very expressive type system[0], which you won't otherwise find before hitting the more functional and research-y side of things. Modelling data in terms of enums (sum types), move types (affine types), etc... makes it a lot more reliable and a lot less faillible as it just removes entire swathes of edge cases. See concepts like "making invalid states unrepresentable".
[0] though it's still lacking in many ways, the more you get the more you want after all
That's not entirely true. As an easy example, Go has a type system but it's relatively anaemic and recreates the "billion dollar mistake" of nil. I have personal experience with Go applications that have broken in production due to nil dereferences, to bugs related to type-switching off `interface{}`, and to the language's inability to enforce exhaustive case statements in general.
While the unique safety features in Rust are generally about memory safety, it still takes a much more safety-conscious approach in general than most languages that are statically typed.
Unlike those exotic options, Rust also comes out of the box with a very powerful free linter (clippy) which you definitely want to use, and has a wide community, library, cargo ecosystem, is very stable and tested etc.
When you total it up, you get a fast, safe program that exhibits 60-70% less runtime bugs than something written in either Python or C++ for a similar development effort. You still need to test of course, but you can focus on major integration and logic bugs instead.
And of course you don't have nullability which is another big one.
I was very surprised to read the article. I think in the code I have in mind the borrow checker would only add overhead.
The most frequent errors I see in the Python webapp code are: 1. logic errors, especially around concurrent DB transactions, 2. type errors (missing values, improperly modeled data), 3. performance problems.
Usually I end up looking at source of libraries and making a list of all exceptions that can be thrown. But of course this is also terribly error prone.
However, I think unchecked exceptions are a mistake in any language for writing _serious_ code. Interestingly, everyone hated checked exceptions in java so now everyone makes new exceptions unchecked. I would say this is not because people disliked the idea of ensuring errors were handled, but the verbosity required to do so meaningfully was annoying, so people took shortcuts to avoid pain by wrapping in unchecked exceptions and in the process introduced "exceptions gone wild" (and now everyone who runs a known java app is familiar with the exception stack trace in logs and in the console).
It's just that in practice, the trade-offs the designers made for easy development made writing large applications next to impossible, unchecked exceptions being one of the major sources of instability. You write very fast buggy code and get excellent productivity initially, but as the program grows you fave a factorial growth of interactions between components, and, because the interface points are not well defined and checked at compile time, what you gained during development is lost under an uncontrolled torrent of subtle runtime bugs.
this is one big exception, because logical bugs are most common and most problematic.
[0]: https://msrc-blog.microsoft.com/2019/07/22/why-rust-for-safe...
The article we've all supposedly read (but clearly some have not) is specifically talking about software written in a mix of C/C++ and Python.
On a language level though (I still hate java) but the type safety was nice and did save you a lot of worry. In retrospect I realize most of the issue was the frameworks available and all the terrible configuration stuff.
I love that today you have so many more options because for a lot of time you do save time on a per line level..
Also that Java’s type system is… not great.
So you have to put in a lot of effort (especially if it was before java 8, to say nothing of Java 5) and you just… don’t get much out of it.
Even more so given the syntactic overhead of defining java types before records (or without Lombok), to say nothing of the performance cost.
Still, when I migrated my Python codebase to Rust I got rid of whole classes of bugs and honestly code faster in Rust on a "per debugged line of code" basis. In Python, every line MUST be run or could trigger an exception (and so many do and it is hard to see). This now requires a test harness that tests every single line which is a huge pain. In Rust, the things that can obviously panic are pretty easy to see and are limited (unwrap, expect, indexing, etc.) making it much easier to write robust code, even with a more limited test suite.