The following answer is firmly in the realm of "opinion" and will invite substantial bikeshedding. It's not really an answer but more of a rant, really. Nevertheless it's something that's been bugging me for a long time and I kind of want to get it out of my chest. Sorry.
I think it's really unfortunate that Rust has mandatory unsafe
blocks.
It's incredible that the language designers constrained all possible UB to five operations: dereferencing raw pointers, accessing raw union
s, mutating static
(global) variables, touching FFI symbols, and of course calling unsafe
-qualified functions. If you write code that does not use any of those capabilities (Safe Rust), you've provably eliminated an entire class of errors in your code.
But if you do have to use those capabilities, you essentially have to say please to the compiler before it'll let you do it, every time. Literally! unsafe
blocks do not change the semantics of operations in them; unsafe
blocks do not, as some people think, "delete run-time bounds checking" or "turn off the borrow checker for this chunk of code".
You could wrap the body of every function in every file of your project in unsafe
and the compiler would emit identical machine code, because unsafe
blocks add no meaning to your code. They're just a magic word that you have to say before doing stuff that is otherwise built into the language. And that's kind of sad.
The people who use Rust for systems-level tasks, where those low-level features make up a significant percentage of LoC, have felt this pain. Some have complained. The pain is real, and the language's ergonomics do suffer.
And for what? As I said in the beginning, the Unsafe subset of Rust is very clearly delimited. Which makes it very easy for programmer tools to prominently highlight Unsafe operations in your code, as every IDE I've tried handsomely does. They don't need the unsafe
block for context, it is possible to statically know from the types of the variables involved or the signatures of the definitions being called what ops are unsafe.
Even with plain-text no-highlight editors, it is already a widespread practice to annotate relevant sites with // SAFETY: <contract description>
comments. This is a thousand times better: those comments include an actual human-readable reasoning and they do not bloody shift your code an indent level to the right and add 2-3 lines of meaningless vertical space.
I wish it were a matter of code style, configurable according to every shop's coding guidelines. We're sadly getting even further in the opposite direction: unsafe_op_in_unsafe_fn
was recently introduced, with a plan to make it an error-by-default in future editions. Even the proponents of the feature acknowledge that some people just throw their hands up and write fn foobar() { unsafe { ... }}
to avoid having to deal with this feature of the language.
As a counterpart, I'd love to see an #![allow(implicit_unsafe_use)]
, instead.
And as a closing note, a paragraph from the Wikipedia article of INTERCAL which I'm always reminded of when writing unsafe
blocks...
INTERCAL has many other features designed to make it even more aesthetically unpleasing to the programmer: it uses statements such as "READ OUT", "IGNORE", "FORGET", and modifiers such as "PLEASE". This last keyword provides two reasons for the program's rejection by the compiler: if "PLEASE" does not appear often enough, the program is considered insufficiently polite, and the error message says this; if it appears too often, the program could be rejected as excessively polite. Although this feature existed in the original INTERCAL compiler, it was undocumented.