That's not true in practice: Unsafe code is clearly delineated and can be 100% correctly identified. In C, usage of dangerous features can occur at any point and is much harder to clearly separate.
Then yes, you can use dangerous features in C at any time, but obviously you can also use "unsafe" at any time. The only difference is that "unsafe" is clearer to recognize. But how much this is worth is unclear. First, if you do not invalidly reduce the discussion to only memory safety, you need to review all other code anyway! But even then, it is also not true that only the code marked with "unsafe" is relevant. This is major myth. The "unsafe" code can cause UB outside "unsafe" and logic bugs outside "unsafe" can cause bugs unsafe. This does not perfectly decouple if you Rust repeat this nonsense over and over again.
Don't get me wrong, I think the unsafe keyword is good idea. But the magical powers Rust fans attribute to it and the "SAFETY" comment they put next to it tells me they are a bit delusional.
This is the wrong understanding of Rust's unsafety encapsulation. For example, no logic bug outside of `unsafe` can cause undefined behavior of Rust std's `Vec` abstraction, which is using underlying unsafe to build.
The point that "because unsafe is used so the entire Rust program is also unsafe" is a real major myth. It's as absurd as saying "because Java runtime using unsafe underlying to build so Java is also unsafe".
Why was the fix to this unsafe memory safety bug [0] only changes to code outside of unsafe Rust blocks?[1][2]
Why does the Rustonomicon[3] say the following?
> This code is 100% Safe Rust but it is also completely unsound. Changing the capacity violates the invariants of Vec (that cap reflects the allocated space in the Vec). This is not something the rest of Vec can guard against. It has to trust the capacity field because there's no way to verify it.
> Because it relies on invariants of a struct field, this unsafe code does more than pollute a whole function: it pollutes a whole module. Generally, the only bullet-proof way to limit the scope of unsafe code is at the module boundary with privacy.
[0] https://social.kernel.org/notice/B1JLrtkxEBazCPQHDM
[1] https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux...
[2] https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux...
[3] https://doc.rust-lang.org/nomicon/working-with-unsafe.html
The point coming back to abstraction and responsibility, in Rust, you can build abstraction that is sound and guarantee memory safety from there. There can be soundness bug inside your abstraction, but it will be a massively smaller surface for auditing and expert required to write such abstraction. Also, when soundness bug appears, the responsibility is solely on the abstraction writer, not the user.
Whereas in C, without those safe abstraction, the surface of doing thing right to avoid memory safety issue is your entire codebase, and responsibility of "holding the knife correctly" is on the user.
If all of the Vec has to be "audited", or checked and reviewed, including all the code that is not inside unsafe blocks, how would the surface be any smaller?
> The point coming back to abstraction and responsibility, in Rust, you can build abstraction that is sound and guarantee memory safety from there.
Isn't it normal for programming languages to support building abstractions that can help with not only memory safety, but general correctness? C is a bit barebones, but lots of other programming languages, like C#, C++, Haskell and Scala support building abstractions that are harder to misuse.
Yes, Rust is not new in term of allow building hard to misuse abstraction, it’s just allow abstraction over memory safety without relying on GC or runtime checks. Rust achieve this by adding capability to enforce shared XOR mutability with its borrowck which C++ couldn’t.
Using rust's definition of unsafe which is roughly "can cause undefined behaviour" then it seems to me isolating use of these features isn't possible. What is C without:
* Dereferencing pointers * Array access * Incrementing signed integers
You can do all of the above without invoking UB, but you can't separate the features in C that can cause UB from the ones that can't.
For Rust safety (meaning no UB) most definitely is a property of the language. If a module does not contain unsafe and the modules it uses that do contain unsafe are implemented soundly then there is no UB.
In C UB is a part of the language.
Also "If .. are implemented soundly" sounds harmless but simply means there is no safety guarantee (in contrast to Fil-C or formally verified C, for example). It relies on best-effort manual review. (but even without "unsafe" use anywhere, there are various issues in Rust's type system which would still allow UB but I agree that this is not that critical)
In C UB is part of the ISO language specification, but not necessarily part of a specific implementation of ISO C. If you argue that the ISO spec matters so much, I like to point out that Rust does not even have one, so from this perspective it is completely UB.
Don't those also depend on implementations being sound? Fil-C has its own unsafe implementation, formal verification tools have their trusted kernels, it's turtles all the way down.
Does make me wonder how easy porting Fil-C to Rust/Zig/etc. would be. IIRC most of the work is done via LLVM pass(es?) and changes to frontends were relatively minor, so it might be an interesting alternative to MIRI/ASan.
The point is that when you start using rust in the real world to get real work done a lot of the promises that were made about safety have to be dropped because you need to boot your computer before the heat death of the universe. The result will be that we end up with something about as safe as C is currently - because CPUs are fundamentally unsafe and we need them to work somehow.
Rust is from the universe in which micro kernels weren't a dead end and we could avoid all the drivers being written in C.
Safe rust isn't slow like Python, Go or Fil-C. It gets compiled to normal native code just like C and C++. It generally runs just as fast as C. At least, almost all the time. Arrays have runtime bounds checks. And ... thats about it.
> The result will be that we end up with something about as safe as C is currently - because CPUs are fundamentally unsafe and we need them to work somehow.
Nah. Most rust is safe rust. Even in the kernel, not much code actually interacts directly with raw hardware. The argument in favour of moving to rust isn't that it will remove 100% of memory safety bugs. Just that it'll hopefully remove most of them.