> But the bug is still almost always in an unsafe block.
That's entirely dependent on how you write your Rust code. If you're derefing an invalid pointer then the bug is usually in how you calculated that pointer value, but the only part that actually requires 'unsafe' is the deref, not the bugged pointer calculation.
Now in properly written Rust code you should be marking all of that code as 'unsafe' in that case and documenting what invariants need to be maintained, but that's entirely on you to do. The only part the compiler actually enforces is that you mark the specific spots where you make use of the operations that 'unsafe' allows.
> The only part the compiler actually enforces is that you mark the specific spots where you make use of the operations that 'unsafe' allows.
Yes, I wish there was a way to mark code as unsafe without also allowing unsafe operations. Its quite common I have some "safe" code that generates values which are eventually used in an unsafe block. If the "safe" code is wrong, my unsafe code will fail in memory-unsafe ways. But there's currently no way to annotate this sort of "safe" code. Rust provides the unsafe keyword - but that keyword is generally reserved for code which needs to actually make unsafe operations (like derefing pointers or calling other unsafe functions).
Yeah I agree with that as well, the unsafe keyword takes on a double-duty which makes it unclear how it's actually intended to be used. The idea that you might mark a function that's only made of safe code as 'unsafe' is not that obvious and not always agreed upon, I've seen plenty of debating about it.
The fact that 'unsafe' also enables all unsafe operations in the function body just makes it even messier, since you definitely _don't_ want that unless the entire body is really nothing but unsafe operations. Thus the "mark a safe function as unsafe" has a clear downside since it allows all the unsafe operations you didn't want to use.
> The fact that 'unsafe' also enables all unsafe operations in the function body just makes it even messier
This changed in the 2024 edition to now fire a warning lint, asking you to wrap your unsafe operations in an unsafe block, even inside an unsafe function.
I think this point gets missed too often in the face of technical things the Rust compiler does.
There was a world where everyone just used the unsafe keyword everywhere, and Rust code crashed & had memory issues all the time.
The big thing Rust did wasn't invent borrow checking or memory safety; it's the ease of use, the defaults, and the social aspect of "if unsafe is used, a memory bug is in the unsafe part".