7 ms·
It's safe as long as the unsafe code didn't mess with pointers in unexpected ways. Your unsafe code still needs to behave in a sane way for any safety guarantee
by michaelmior 10y ago
It's safe as long as the unsafe code didn't mess with pointers in unexpected ways. Your unsafe code still needs to behave in a sane way for any safety guarantees to hold.
- fnord123 10y agoSure. And if there's a problem, the search for the bug should be limited to the unsafe blocks. Which is the promised benefit over C++.
- Animats 10y agoNo. As soon as there's unsafe code, there's the possibility of safe code misusing the unsafe code to create unsafe behavior. One would like to have un-abusable unsafe code, but the language does nothing to guarantee that.
- pcwalton 10y agoThe parent post is correct. Unsafe code must uphold the invariants of safe Rust. If unsafe code is safe only if the caller upholds some invariants not enforced by the compiler, then by definition it's the unsafe code that is wrong.
- Animats 10y agoUnsafe code must uphold the invariants of safe Rust. Ideally, yes. In practice, maybe. We're probably going to see "unsafe" code that assumes good behavior on the part of the caller. That's a classic problem with APIs.
- pcwalton 10y agoThere's no way to solve that problem without just forbidding unsafe code entirely. Unsafe code can have bugs; that's why you should keep it to the minimum and keep it well-known and audited. In this case, the byteorder crate would have been more appropriate than handrolling unsafe code.
- Animats 10y agoThere's no way to solve that problem without just forbidding unsafe code entirely. That's not at all clear. It's worth looking at unsafe code and asking "why was this necessary"? What couldn't you do within the language? As patterns reoccur, it may become clear what new safe primitives are needed.
- pcwalton 10y agoWhy is it better to add safe primitives directly to the compiler rather than implementing them in libraries?
- Animats 10y agoThe compiler can look at more data to decide if something is valid, or can be optimized. C++ is trying to add move semantics via templates, but can't get all the way to Rust's borrow checker that way.
- Manishearth 10y agoRight, but in this case it doesn't need to. I have yet to see an example of an operation that: - should be safe in Rust but isn't - needs /compiler/ support to work well (can't be done cleanly as a library) - isn't already on the track for implementation (non-lexical lifetimes, SEME regions) You did mention uninitialized arrays but uninitialized data is inherently unsafe. It's not an operation that can be made safe. Instead, you make it safe by encoding the invariants specific to your use case in your code and creating a safe wrapper -- these invariants differ by use case, so it can't be made a generic operation.
- Animats 10y agoYou did mention uninitialized arrays but uninitialized data is inherently unsafe. It's not an operation that can be made safe. Sure it can. You just need primitives which can be used in asserts such as is_initialized(tab,i,j) indicating that an array is initialized within those limits. Then you can write asserts such as assert(is_initialized(tab,i,j-1)); ... initialize tab[j] assert(is_initialized(tab,i,j)); Standard program verification technology. Verification of unsafe sections is a useful goal, and deserves language support. Hand-waving about "encoding the invariants specific to your use case in your code" is insufficient. You need to write them down and prove them. Then you can eliminate them from the run-time code.
- Manishearth 10y ago> We're probably going to see "unsafe" code that assumes good behavior on the part of the caller. I have yet to see any of this. I have noticed that it's harder to write correct unsafe code when it comes to parallelism and FFI, but parallelism has always been a hard problem and the FFI problems generally come from the fact that you need to know the invariants being upheld on the other end, which is trickier. But for this kind of unsafe code -- designing (non-parallel) abstractions -- upholding invariants is pretty straightforward.
- Gankro 10y ago> I have yet to see any of this. mem::forget-pocalypse was this. (Rc/Arc, Vec::drain, thread::scoped) Any UB bug that results from an overflow is kind've implicitly this. BTreeMap::range still has an UB bug from trusting the caller! I literally asked you to fix it! https://github.com/rust-lang/rust/issues/33197 https://github.com/rust-lang/rust/issues/33197 Bugs happen man.
- Manishearth 10y ago> mem::forget-pocalypse was this. I would say that this is from a time when the invariants were not understood. In particular, the fact that leaking is safe to do in safe code was not known. (The invariants are still not completely understood, but there's work to specify that, and IMO they're understood enough to be able to avoid unsafe bugs) > BTreeMap::range still has an UB bug from trusting the caller! Fair :) I'd completely forgotten about that one.
- Gankro 10y ago> I would say that this is from a time when the invariants were not understood. Yeah, but it's not like "oh this is an obvious thing to consider trusting the caller about". It's an exceptionally niche problem that you'd only know about if someone told you about it. Especially since a Rust programmer shouldn't be expected to write unsafe code often, if ever! Similarly: not trusting traits to be implemented correctly. Not trusting closures to not-unwind.
- staticassertion 10y agoThe unsafety still originates in the unsafe code, thus you can audit the use of the unsafe code or the unsafe code itself just the same.
- Manishearth 10y ago> there's the possibility of safe code misusing the unsafe code to create unsafe behavior. This is a misconception. You can write un-abusable unsafe code pretty easily. There are a couple of invariants that the language requires you to uphold. As long as you uphold them you are fine. If your unsafe code can be broken by external safe code that is a bug in your unsafe code, on par with doing `let p = ptr::null(); print(*p);` in your unsafe code.