4 ms·
The second point — ability to wrap `unsafe` code in safe APIs — is especially important. Ability to isolate high-risk "hold my beer" code from boring glue code
by pornel 3y ago
The second point — ability to wrap `unsafe` code in safe APIs — is especially important.
Ability to isolate high-risk "hold my beer" code from boring glue code has many nice side effects.
In a team, you can agree who can write and review `unsafe` code, and not be afraid that your junior team members will cause some horror heisenbug (there can always be other types of higher-level bugs in safe code, but safe code doesn't cause them, you'd have them anyway, on top of memory issues).
People often move abstractions built from unsafe code (like clever data structures, concurrency primitives, OS or C library wrappers) into separate crates. This makes it easier to thoroughly test and fuzz just the unsafe code, without having to test a larger project as a whole (once you ensure your safe API is indeed upholding safety invariants, the compiler will prove all uses of that code are safe too).
- throwaway894345 3y agoI'm curious if a linter + the new C23 annotation syntax could be used for a similar purpose in C? EDIT: I'm rate limited. If we assume an `[[unsafe]]` attribute would only apply to things like pointer arithmetic and unchecked array access (not lifetimes), could such a linter exist?
- Conscat 3y agoUnfortunately the attributes are not nearly rich enough. There are many proposals for massively increasing the information that you can give C compilers, such as the lifetime safety profile and n3005, but it's a very difficult problem to do this in a language that makes pointer aliasing and pointer arithmetic very easy. n3005: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3005.pdf https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3005.pdf
- galangalalgol 3y agoEven of the error originates in unsafe code, the UB can manifest elsewhere. The optimizer makes assumptions that you can easily invalidate in an unsafe block leading to UB. In many ways it is mich easier to introduce UB in an umsafe block than it is in C++. But at the very least you know the UB originated in one of your unsafe blocks. Its just figuring out which one. I very rarely use unsafe outside bindings though, so that helps too.
- GolDDranks 3y agoThe nice thing is that you can check the invariants at some reasonable module boundaries. The bugs in unsafe code can manifest elsewhere, but at least, when wrapped properly, someone _else's_ bugs won't cause your unsafe code to have UB.
- DSMan195276 3y agoThe problem I personally have is the "when wrapped properly". Functionally it's the same issue as "just write correct C code", but the invariants that have to hold in Rust aren't as well defined (because there's more of them). Writing "correct" `unsafe` code is a lot harder than it seems on the surface. There's also the issue that I've seen it commonly advocated to keep `unsafe` blocks small - which makes intuitive sense, but brings into question whether `unsafe` is supposed to _just_ mark the unsafe operations being done, or mark all the code under which certain assumptions have to hold for UB to be avoided and it be 'safe'. It's been a little while since I looked into it, but there wasn't much agreement on this the last time I looked and I've seen people do it both ways.
- Veliladon 3y ago"When wrapped properly" is a lot more likely to happen in Rust because idiomatic Rust involves things like proper error handling and returning Result for fallible operations. I use libraries that have gobs of unsafe all the time. Wgpu is basically an unsafe code factory because it glues Rust to C and C++ but all of its unsafe code returns Options and Results because Rust programmers get them all drilled into them by the community and all the learning resources said community provides.
- GolDDranks 3y agoHave you written unsafe code in Rust? I have some experience writing unsafe code, and most of the time you can do it without problems, wrapped in sensible safe APIs that don't allow unsafety to leak. Of course, this needs experience and knowledge about API design and the invariants that must be followed... and preferably another set of eyes. The thing is that once you've managed to do that, the _consumers_ of your API are free from worries. The situation becomes a whole lot better than the equivalent C situation, where both sides of the API need to be careful about what funny abominations the another side might devise.
- bobbylarrybobby 3y agoA point in support of this stance is that the entire standard library is a safe wrapper over unsafe code. Vec does fiddly things with pointers and memory management so that you don't have to!