← Back to context

Comment by saghm

2 hours ago

> Stdlib usage is full of it, and it must be by definition of what it does. In the same way you can't have a useful program without some side effects, so also you can't really have a useful program without doing some level of I/O and FFI, and I/O/FFI is always going to use unsafe under the covers.

Yep, it's not uncommon for people to misunderstand the point of Rust to be that nothing is ever unsafe. The point is that you can build safe abstractions on top of unsafe code, which narrows down the potential for those types of bugs to specific areas so that they can ideally be very small so that you can audit them more effectively to try to eliminate them (or at least more easily track them down if they do happen). In practice, I think the amount of code that actually needs to be unsafe is a lot smaller than even people who are aware of this might expect, because even if an API is unsafe, you can often push it low enough that a wrapper on top of it can enforce at least some of the invariants needed to use it safely.

I haven't needed to write unsafe code for the work I do very often (outside of stuff like FFI which often is also abstracted in a way that reduces the need for doing it manually), but when I do, I document each unsafe API with a list of the invariants needed to uphold for safe usage (which is pretty common) and then document each usage of it with a full proof of why I conclude that it's safe (which is somewhat common but more often it seems to be a single statement that isn't super rigorous rather than a list of statements that explain the full deduction of how to conclude whether each invariant is met). At the end of the proof, if there's at least one invariant that I haven't been able to prove, it means the API around whatever code I'm writing also needs to be unsafe and document for the caller needs to use it. Most of the time there's some low-hanging fruit for making some of them correct by construction though, e.g. by wrapping up the data in a struct that can only be created from outside the module with a constructor that upholds the variants correctly.