Why do the standard libarary have so many internal layers?
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?