Another dimension to it is that the borrows checker rules also apply in most other languages if you want to write valid code. They are just not enforced and you have to rely on your own discipline instead (which humans are bad at).
The problem with unsafe programming in the traditional C style (with its heavy reliance on shared, mutable, possibly aliased pointers) is that it's inherently non-compositional: proving safety or correctness of such a piece of code is a 'global' problem that cannot be meaningfully decomposed or modularized. That's why Rust's choice of explicitly restricting these features to "unsafe" blocks feels intuitively right - the "safe" part of Rust is essentially the part that can be worked with in a highly modular way, where safety is checked 'locally' via a type system. Future versions of Rust may well make some currently-unsafe features slightly easier to use, perhaps even more idiomatic in a way that might appeal to C developers, but some inherent constraints will remain.
Yes, ARM processors are used more and more, but there are really A LOT of embedded systems out there, which are still, for example, 8 bit. As an embedded dev, who is using pretty much everything from 8 to 64 bit, the kind of memory safety errors you talk about I have yet to see with on anything based on (say) a 8051 or PIC. On a system without OS and without dynamic memory allocation, the borrow checker does not buy you much. It's a different story with an ARM processor and an OS (real-time or not does not matter).
Of course Rust has a more powerful type system, but my experience is, that the preferences for a C type type system dominate the embedded world. Not because it is better or simpler, but because most embedded devs actually like low level. They are simply not interested in type driven development (yes it's just their personal taste) and its benefits.
Or let me say it like this. I see two kinds of embedded devs:
- Those who abstract way the problem ("It's all just a big char array.", "It's all just ints.", "It's all just data structures").
- Those who abstract away the machine ("It's all just objects.", "It's all just types.").
Right now, my observations tell me that the first group is still dominating. Time will tell if this is going to change.
As an embedded dev one of the things I love about Rust is the ability to use the type system to enforce the machine's invariants. Some register has a bunch of bit fields? Break it up using a struct, and unlike C's struct bitfield notation you don't have to shift the values around every time.
Rust forces you to check all enum variants, not just in switch statements like C (and that only with the -Wswitch-enum or equivalent warning) and disallows any unlisted value (there's no "default" for an enum).
Those things alone probably aren't enough to switch, but as a machine-focused embedded dev they're certainly nice to have.
> I think this profession is hard enough as it is.
Exactly, I think so too. Don't think I need another source of annoyance like spending days on digging at obscure segfaults and data races caused by logical mutability mistakes.
My approach of learning any language with "difficult syntax" is to treat those as language features rather than a burden.
It is the language bridging the coder to the complexity under the hood (the computer). This comes from Rust's nature of being a low-level PL, and it is really awesome to be able to abstract those minute details so that one can write Rust and "look, it's not that different from JavaScript"
Another analogy would be `gerund` in English. English has the `gerund` feature for its speakers to nounify a verb. Other languages don't have that, some even use the same symbols for both the verb and the noun form.
Both Rust's "difficult syntax" and English's gerund needs only one-time learning session. Once you're fluent on it the sentences come out on their own.
But over the time this argument loose fairness in some direction. Today, in 2020, I would feel awful if in my meetings I argued that creating unit tests is meaningless, as this will point bugs in my code that I need to fight with. Or that QA is a bunch of mean people that do not understand how to use my library and only want to point to "bugs" that is more work to me to "fix".
IMHO the borrow checker _is_ this test that I do not want to write, or this QA engineer that exercise the code to find a corner case when I am causing a memory corruption in my not-perfect work.