Why do the standard libarary have so many internal layers?

237 points chokomancarr 60 comments

When I was reading the source code of various standard library functions, I often need to jump through more than multiple hoops to find the actual implementation. Why is this the case?

Take the function std::mem::swap. Internally it is defined as:

pub const fn swap<T>(x: &mut T, y: &mut T) {
    // SAFETY: `&mut` guarantees these are typed readable and writable
    // as well as non-overlapping.
    unsafe { intrinsics::typed_swap_nonoverlapping(x, y) }
}

... which in turn calls ...

pub const unsafe fn typed_swap_nonoverlapping<T>(x: *mut T, y: *mut T) {
    // SAFETY: The caller provided single non-overlapping items behind
    // pointers, so swapping them with `count: 1` is fine.
    unsafe { ptr::swap_nonoverlapping(x, y, 1) };
}

... which in turn calls (after much safety checks) ...

let slice = slice_from_raw_parts_mut(x, count);
// SAFETY: This is all readable from the pointer, meaning it's one
// allocation, and thus cannot be more than isize::MAX bytes.
let bytes = unsafe { mem::size_of_val_raw::<[T]>(slice) };
if let Some(bytes) = NonZero::new(bytes) {
    // SAFETY: These are the same ranges, just expressed in a different
    // type, so they're still non-overlapping.
    unsafe { swap_nonoverlapping_bytes(x.cast(), y.cast(), bytes) };
}

... and on and on, until it eventually calls this:

fn swap_chunk<const N: usize>(x: &mut MaybeUninit<[u8; N]>, y: &mut MaybeUninit<[u8; N]>) {
    let a = *x;
    let b = *y;
    *x = b;
    *y = a;
}

Why is the standard library going through all the hoops? Why not just write this (or a swap of transmuted bytes) directly in std::mem::swap? Since it already takes 2 mutable references, surely all the non-overlapping / alignment UB checks would be unnecessary?