The Rustonomicon
doc.rust-lang.org
doc.rust-lang.org
> The Dark Arts of Unsafe Rust
> THE KNOWLEDGE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF UNLEASHING INDESCRIBABLE HORRORS THAT SHATTER YOUR PSYCHE AND SET YOUR MIND ADRIFT IN THE UNKNOWABLY INFINITE COSMOS.
I know it's a joke, and I laughed when I first read it. Unsafe is dangerous and requires care, but unsafe == evil is definitely not true. Jon Gjengset said it well in "Demystifying Unsafe Code" [0]:
> One thing I keep observing about the Rust community is how we're all like allergic to unsafe code or we think that unsafe code is is totally fine and not nothing to worry about, and I wanted to take a little bit of time to talk about what unsafe is because I think a lot of where the communities lack of alignment on this particular topic comes from a fact that unsafe is a bit of a mystery to many of us
Learning about unsafe is useful, and at time necessary, it's not inherently evil, nor should it be used too freely.
I am suggesting this to prevent the likes of the burn-out that happened with actix-web and its use of unsafe (which is now fixed apparently)
However a sufficiently determined evil crate can use soundness holes (like fake-static) or macros (like plutonium) to misbehave without visible unsafe.
Nothing really can stop a truly determined bad actor completely, but I don’t think that was GP’s point, rather that it’s good to easily know the potential risk you are exposing yourself to with your dependencies in a practical sense.
It's just a tool of course, so the perspective that it's neither bad nor good is justified to an extent. But it's not that unsafe is complicated, it's that it's full of unknown unknowns.
If you don't know any better, it's _really_ easy to write something not exception-safe, accidentally trust safe code a little too much to maintain invariants in your in unsafe code, or to break some other subtle assumption that prevented LLVM from merrily miscompiling what's in your head.
I've actually found the reverse problem to be even more detrimental: people coming from high level languages like Python are getting tripped up because they try to go too low level to make up for tradeoffs they no longer have to make. Rust makes it painfully obvious when wrapping a shared value in a Rc+RefCell/Arc+Mutex or cloning so it seems like an antipattern to newbies. Even though the majority of them are coming from languages where everything is implicitly reference counted and performance is abysmal, they worry about incrementing an atomic integer or acquiring a lock in a program that will only ever run on x64. I can't imagine what they'd come up with using `unsafe` if the community didn't literally refer to it as the dark arts.
(That said, you obviously still have to be cautious when doing FFI.)
I was one of the people who commented on that post. My point was basically that "you need to uphold invariants in order for this to be sound" is exactly what `unsafe` means in Rust. So if you're wrapping a library that doesn't guarantee safety then you should mark it as unsafe (and there's nothing wrong with that).
Rust library users will typically assume that they can do absolutely whatever they want with a safe interface and they cannot possibly cause memory safety issues, undefined behaviour, etc. A large part of the benefit of Rust is not even having to think about that. So it's important that libraries continue to stick to this convention.
However, creating bindings without typing out `unsafe` was a controversial issue, discussed at https://www.reddit.com/r/rust/comments/ielvxu/the_cxx_debate... .
Also there are tools to automatically parse C++ headers and turn them into cxx bindings: https://github.com/google/autocxx and https://docs.rs/autocxx/
The quote doesn't say it will unleash indescribable horrors. Just that it provides no warranty that it won't. That's entirely fair considering what optimizing compilers will do when you break a contract. So it doesn't say unsafe == evil. It just says evil things may or may not happen when you use unsafe.
I think linked lists are a great example of something that causes Rust's ownership model to fall apart. I've seen it done with tradeoffs, but it's something that you're best off implementing with pointers and unsafe blocks (though you probably should just use a battle-tested implementation or consider if you'd be better off with a vector for cache locality).
It's worth checking https://plv.mpi-sws.org/rustbelt/ghostcell/ and https://github.com/matthieu-m/ghost-collections for an alternate approach that's currently being worked on.
Quite non-intuitive and it has yet to be proven 100% safe, plus it doesn't actually obviate everything you might want to do w/ potentially-aliased pointers, meaning that some desirable patterns are still off-limits - but it has the best chance of working out so far.
For example, https://doc.rust-lang.org/nomicon/send-and-sync.html says:
> TODO: better explain what can or can't be Send or Sync. Sufficient to appeal only to data races?
Coincidentally I happen to have just written about this topic a few months ago: https://nyanpasu64.github.io/blog/an-unsafe-tour-of-rust-s-s...
Update 2: The changes to the GitHub repository are only being reflected in the nightly Nomicon, not the main webpage. Link to update: https://doc.rust-lang.org/nightly/nomicon/send-and-sync.html...
Update 3: Perhaps they are being reflected in the main webpage, but with a delay (possibly once per language release?)
If you're interested in this sort of thing John Gjenset's YouTube channel is also a great resource.
> This is really, truly, the most horribly unsafe thing you can do in Rust. The guardrails here are dental floss.
- Transmuting an & to &mut is always UB.
- No you can't do it.
- No you're not special.
Anyway, I thought the passage was an interesting reflection of the perspective of the 'nomicon authors. I'm fairly new to rust, and I've observed the culture – or at least, some part of the culture, and only as it's visible to newcomers – to be astonishingly friendly and helpful, but also sometimes, well, condescending. I sometimes get the occasional vibe of "this is bad and you want a bad thing" instead of "let me acknowledge your concern and here're options for addressing it" (with all the usual caveats: this attitude isn't by any means universal or even exemplified by a particular individual; obviously, the rust community doesn't owe anyone anything, so don't interpret the complaint as a demand; &c).
I wonder whether other people have had a similar perception.
My feeling isn't really about `unsafe` in particular, though; it's more general. (Although I do see the potential conflation of "safe = correct, unsafe = incorrect" as problematic.)
I also feel "moralistic" about soundness, and I do see that in the community. You could argue that such moral thinking is misguided, IDK.
And there are cases where people deliberately use UB to improve performance, like https://internals.rust-lang.org/t/bit-wise-reasoning-for-ato... and https://internals.rust-lang.org/t/unordered-as-a-solution-to.... I'm not sure how the core team, or community, views such situations.
Additionally, async fn desugarings and Tokio's intrusive linked lists (self-referential objects that can create &mut references) are unsound under Stacked Borrows (Rust's currently popular "formal memory model" for deciding what code/optimizations are sound), though I've heard promises that Stacked Borrows will be adjusted to declare these as sound.
Apologies if you're already familiar with this: It doesn't mean it's not sometimes necessary to have two `*mut T` to the same location, it's that the compiler makes specific assumptions about `&mut T` such that two &mut to the same location existing, even if never used, is UB. To work around this you need to stay in raw pointer land and use APIs like ptr::write.