2 ms·
If you use unsafe Rust, you do give up the advantage of being able to say, "Look at this code, no unsafe, therefore no UB." However, the lifetime and ownership
by oconnor663 8y ago
If you use unsafe Rust, you do give up the advantage of being able to say, "Look at this code, no unsafe, therefore no UB." However, the lifetime and ownership model is still doing a lot for you. You get a lot of control over the way that your safe code (hopefully most of your program) can interact with your unsafe code. For example if I have this function (partially copied from listd):
fn reverse_slice<T>(slice: &mut [T]) {
let len = slice.len();
for i in 0..len / 2 {
// Unsafe swap to avoid the bounds check in safe swap.
unsafe {
let pa: *mut T = slice.get_unchecked_mut(i);
let pb: *mut T = slice.get_unchecked_mut(len - 1 - i);
std::ptr::swap_nonoverlapping(pa, pb);
}
}
}
In theory, I could use those unsafe pointers to alias the same value and cause UB. Or I could stash them somewhere that outlives the lifetime of what they're pointing to, and cause UB later on that way. So I have to audit the function carefully to avoid doing those things. But at the same time (in the absence of other broken unsafe code in the caller), this function can rely on a lot of guarantees:
- The `slice` reference is guaranteed to be unique. No other code, on any thread, has read or write access to `slice` while this function is running.
- The length of `slice` is guaranteed to be correct. All of the memory it refers to has been properly initialized, and computing pointer offsets into it cannot overflow `usize` or otherwise cause UB. (This guarantee is surprisingly subtle, because it means the maximum length of a slice is `isize::MAX` rather than `usize::MAX`.)
- The type `T` is guaranteed to be safe to move. It doesn't contain any internal references to its own memory, which moving would invalidate.
All of this put together makes it possible to audit this function by itself, to make sure safe code can't use it to trigger UB.