Copying my post from Tracking issue for the to_bytes and from_bytes methods of integers · Issue #49792 · rust-lang/rust · GitHub
Two recent internals threads with thoughts around this area:
It seems to me like there's a general common theme here of "safe, but a bit weird and rather transmute-y" conversions: this thread's uN
<=> [u8; N/8]
, the first thread's u16x8
<=> u32x4
and u32
<=> f32
, some parts of as
like u32
<=> i32
that currently don't have a method version, and extended versions of that like &'a u32
<=> &'a i32
that are never exposed as safe today (but could be).
So here's a sketch of an idea using #[marker]
traits:
#[marker] unsafe trait InplaceReinterpretAs<T> {}
unsafe impl<T> InplaceReinterpretAs<T> for T {}
unsafe impl InplaceReinterpretAs<[u8; 4]> for u32 {}
unsafe impl InplaceReinterpretAs<i32> for u32 {}
unsafe impl InplaceReinterpretAs<u32> for i32 {}
unsafe impl<T, U> InplaceReinterpretAs<*const U> for *const T {}
unsafe impl<T, U> InplaceReinterpretAs<*mut U> for *mut T {}
unsafe impl InplaceReinterpretAs<u16x8> for u32x4 {}
unsafe impl InplaceReinterpretAs<u32x4> for u16x8 {}
#[marker] unsafe trait ReinterpretAs<T> {
// Because it's a marker trait, these cannot be overridden,
// and thus their behaviour is always predicatable
fn reinterpret(self) -> T {
unsafe {
let r = ptr::read_unaligned(&self as *const Self as *const T);
mem::forget(self);
r
}
}
unsafe fn reinterpret_unchecked(x: T) -> Self {
let r = ptr::read_unaligned(&x as *const T as *const Self);
mem::forget(x);
r
}
}
unsafe impl<T, U> ReinterpretAs<U> for T where T: InplaceReinterpretAs<U> {}
unsafe impl<'a, T, U> ReinterpretAs<&'a U> for &'a T where T: InplaceReinterpretAs<U> {}
unsafe impl<'a, T, U> ReinterpretAs<&'a mut U> for &'a mut T where T: InplaceReinterpretAs<U> {}
unsafe impl ReinterpretAs<u32> for [u8;4] {} // not ok in-place, but fine as memcpy
Certainly std
is generally adverse to introducing these using traits, but I think the recursiveness of the scenario makes the trait version more compelling than normal in this case. If one can turn a u32
into a [u8; 4]
safely, why not also be able to turn a &[u32]
into a &[[u8; 4]]
safely?
(Name inspired by C++'s reinterpret_cast
, obviously.)