4 ms·
"unsafe" is a promise to the compiler that you're going to ensure invariants that the compiler can't check. Rust only promises to eliminate UB if the invariants
by AlotOfReading 5mo ago
"unsafe" is a promise to the compiler that you're going to ensure invariants that the compiler can't check. Rust only promises to eliminate UB if the invariants are held. You can still get UB by violating that promise, as this bug demonstrates.
- stavros 5mo agoBut the title here says "in safe Rust", no? Is the unsafe code causing UB in safe code? I thought the unsafety couldn't "spread" like that in Rust.
- dralley 5mo agoIf you use unsafe improperly, it is possible to encounter UB in "safe" code which relies on the unsafe code being correct.
- repelsteeltje 5mo agoUB != unsafe
- chiffaa 5mo agoUnsafe code can break certain invariants of Rust, as `unsafe` is just a compiler "hold my beer" flag, which is why you're meant to do safety checks in your safe interface around unsafe code. If the unsafe code is wrapped in a way that does no guarding (or does something stupid in general), it is technically marked safe (because you said "rustc, hold my beer" as `unsafe` is also a contract) despite actually being unsafe
- Xylakant 5mo agoThat is not Rusts guarantee. The guarantee is that safe rust cannot in itself introduce UB - UB can only ever be introduced in unsafe blocks, but it can then materialize in safe code.
- stavros 5mo agoAh OK, that makes sense, thanks.
- krona 5mo agoI can't tell if you're trolling but `unsafe { crash() }` is safe from the compiler's perspective. Otherwise you wouldn't be able to achieve anything in 'safe' rust, even print to stdout.
- flomo 5mo agoI think its a good question, just because the whole UB thing is such an ideological shibboleth. Maybe its better to think about this in the reverse, where C and C++ has 'defined behavior', but unsafe rust intentionally does not, its just whatever the complier and platform lets you get away with. Ultimately its still just a computer which stores values in memory and jumps to subroutines.
- AlotOfReading 5mo agoEvery language has defined behavior. It's what you expect to happen through a program's execution. Sometimes there will be multiple possibilities, but you can still define them regardless. Laying this out explicitly is the purpose of a standard. Undefined behavior is everything else. C and C++ are relatively unique in that their standards explicitly say "combining these constructs in this way is undefined", and we call those cases explicit UB. There's also a larger universe of implicit UB that standards omit. Most (all?) languages have implicit UB, even if they lack the explicit stuff. What happens when you get ENOMEM is a common one. Rust does something similar to C/C++ and lists a bunch of UB that's only possible with incorrect code in unsafe blocks. Correct code placed in an unsafe block remains defined, as does code without unsafe (up to compiler/language bugs).
- flomo 5mo agoYeah, if I understand correctly, the Rust project has no intention to formally 'define' what unsafe actually does, so its very implicit. Could be anything... so it's the Does It Work? standard.
- bfeynman 5mo agoit's more straightforward to write safe rust when rust owns everything, In real world you often are interfacing with underlying libs or systems etc, which you need to treat as invariants but also handle yousrelf manually to make guarantees to compiler. unsafe exists in tons of codebases it's just you have to make sure you encapsulate it properly, which is what this bug is.
- muvlon 5mo agoIt can spread into safe code when you build an incorrect "safe" abstraction around unsafe code. Which the Bun Rust port apparently has.
- MereInterest 5mo ago> I thought the unsafety couldn't "spread" like that in Rust. The goal of a library is to provide the encapsulation such that the unsafety doesn't spread. If undefined behavior occurs, the fault lies with whoever wrote `unsafe { ... }` in the body of a function. If I write "unsafe" in order to call an unsafe library function, and I don't meet the library function's pre-requisites, then it's my fault. If the library internally writes "unsafe" in order while providing a safe wrapper, and I never actually wrote `unsafe { ... }`. If neither I nor the library wrote `unsafe { ... }`, then it is the fault of the compiler. Using "in safe Rust" means that `unsafe` doesn't occur either in the user code nor in the library. In this context, since we've heard how many uses of `unsafe { ... }` exist in the Bun rewrite, I'd read "in safe Rust" to mean "without calling any functions marked as unsafe".