4 ms·
Off the top of your head, do you have simple examples of this? I know a bit of Rust but I assumed (apparently wrongly!) that safe-within-unsafe should just work
by neongreen 4y ago
Off the top of your head, do you have simple examples of this? I know a bit of Rust but I assumed (apparently wrongly!) that safe-within-unsafe should just work.
- zozbot234 4y agoIt will work if your unsafe code does not actually opt-in to any unsafe features-- in which case you would not actually need an unsafe block. If it does use any of those however, it's very easy to break the expected preconditions of safe code and create unsoundness, e.g. around references (since code written in safe Rust has little use for raw pointers) or properly initialized data (Rust expects you to use MaybeUninit<> whenever data may not be properly initialized but that's a mere wrapper, which can only be removed by unsafe code).
- littlestymaar 4y agoSo if your unsafe code hits an UB then your safe code can be broken…
- kibwen 4y agoOf course, undefined behavior anywhere in the program automatically makes the entire program invalid (this is how undefined behavior works in C and C++ compilers as well). But that's not what the parent commenters are talking about. An `unsafe` block in Rust represents a place where the ordinary invariants of the language may be violated. This is immensely useful for auditing, documentation, and manually verifying that the program acts as you expect. But it's a common mistake to assume that this also means that future changes to non-unsafe-blocks cannot invalidate existing unsafe blocks. While it is always necessary for an unsafe block to exist as a sort of "root cause" of unsafety, safety invariants can rely on things that are merely reachable from unsafe blocks. In practice, this means that the boundary for safety in Rust is not the unsafe block itself, but rather the boundary of the module that contains the unsafe block. This also means that, if you're writing unsafe blocks in Rust, it behooves you to make their containing modules as small as possible in order to reduce the amount of things the unsafe block can reach, and therefore reduce the number of changes that might accidentally change an assumption that an unsafe block is relying upon.
- littlestymaar 4y agoI know about that (even though it's a worthwhile addition to this thread for other readers), but I don't think it's what they had in mind, since they were talking about “Rust devs get bitten by this all the time when trying to reuse "safe" library code within an unsafe context” in their original comment[1], and I really can't see what they are talking about except some variation around “from C++, I passed some uninitialized memory to a Rust library and Rust went boom” but maybe I'm just misunderstanding. [1]: https://news.ycombinator.com/item?id=32878775 https://news.ycombinator.com/item?id=32878775
- tsimionescu 4y agoIsn't it easy to call safe functions from unsafe Rust that, if called from safe Rust, would lead to a compilation error? For example, accidentally passing a safe function two mut pointers to the same object, which normal Rust ownership wouldn't allow you to?
- paavohtl 4y agoTechnically speaking it is OK to pass two aliasing mut _pointers_ to a (safe) Rust function because safe Rust can't do anything dangerous with raw pointers; both dereferencing and writing through a raw pointer require unsafe. If we are talking about references instead, creating two mutable references to the same object in Rust (unsafe or not) immediately causes UB.
- kibwen 4y agoI can't think of any such thing, no. Unsafe Rust doesn't turn off the borrow checker, it just lets you do a handful of operations that you can't do otherwise. The only way I could see that happening would be if you somehow violated ownership invariants in the unsafe block, which is already forbidden.
- ekidd 4y agoI'm not sure what the grandparent post if referring to, but: - In general, "unsafe" code inside a module may depend on invariants maintained by "safe" code in the same module. For example, the "length" field in a Vec can be changed by the safe internals of Vec. But if the safe code in Vec sets an invalid "length" value, then unsafe code relying on "length" might fail. So once you find an unsafe block, you may need to audit some of the surrounding safe code in the same module. - Unsafe Rust is actually slightly less forgiving than C or C++, partly because Rust is allowed to set "noalias" on lots of immutable references, IIRC. The Rustonomicon talks a lot about issues like this.