The best tool out there, imho, to do this kind of unsafe
/ dangerous casts is with ::zerocopy's traits:
-
Add a #[derive(FromBytes, Unaligned)]
on your #[repr(C)]
struct;
- (you do not need
Unaligned
if you know the bytes that will be reinterpreted are already well-aligned),
-
Call
LayoutVerified::new(byte_slice)
.expect("slice of incorrect length or alignment")
to construct a LayoutVerified<&'_ [u8], YourStruct>
, which is a wrapper type around the &'_ [u8]
byte slice (i.e., a type-level annotation) that expresses that such (wrapped) byte slice can be reinterpreted as a &'_ YourStruct
example
use ::zerocopy::{
FromBytes,
LayoutVerified,
Unaligned as OneAligned,
};
#[derive(
Debug,
Clone, Copy,
PartialEq, Eq,
FromBytes, OneAligned,
)]
#[repr(C)]
struct Foo {
x: u8,
y: u8,
}
fn main ()
{
let byte_slice: &'static [u8] = b"hi";
let foo: &'static Foo = LayoutVerified::new(byte_slice).unwrap().into_ref();
assert_eq!(*foo, Foo { x: b'h', y: b'i' });
}
Regarding the owned variant, however, is more complex, mainly because of alignment. Here it is very highly advised that YourStruct
be Copy
to ensure lack of drop glue, and it is imperative that the alignment of your struct be 1
, i.e., that you do have the Unaligned
derive on your struct to get a compile error when it is not the case. Then, when the length of the vec
is equal to the mem::size_of::<YourStruct>()
, you:
-
.into_boxed_slice()
the vec
to drop any extra capacity allocator-wise.
This yields a Box<[u8]>
.
-
Box::into_raw()
it go get a (fat) raw pointer: *mut [u8]
.
-
.thin()
it down to a *mut u8
, either by doing as *mut u8
(dangerous: little type safety there), or through some helper method:
trait ThinYourPointersWithThisSimpleTrickCallNow {
type Ret;
fn thin (self) -> Self::Ret;
}
impl<T> ThinYourPointersWithThisSimpleTrickCallNow
for *mut [T]
{
type Ret = *mut T;
#[inline]
fn thin (self: *mut [T]) -> *mut T
{
self as _
}
}
// now you can do:
fat_ptr.thin()
-
.cast()
that pointer to *mut YourStruct
.
-
Upgrade it to Box
, through a(n unsafe
) call to Box::from_raw
.
example
use ::core::{mem, ops::Not as _};
use ::zerocopy::{
FromBytes,
LayoutVerified,
Unaligned as OneAligned,
};
#[derive(
Debug,
Clone, Copy,
PartialEq, Eq,
FromBytes, OneAligned,
)]
#[repr(C)]
struct Foo {
x: u8,
y: u8,
}
fn main ()
{
let vec: Vec<u8> = b"hi".to_vec();
// Feel free to factor that out into its own function:
let boxed_foo: Box<Foo> = {
assert_eq!(1, mem::align_of::<Foo>());
assert_eq!(vec.len(), mem::size_of::<Foo>());
assert!(mem::needs_drop::<Foo>().not());
let boxed_slice: Box<[u8]> = vec.into_boxed_slice();
let fat_ptr: *mut [u8] = Box::into_raw(boxed_slice);
let thin_ptr: *mut u8 = fat_ptr.thin();
let foo_ptr: *mut Foo = thin_ptr.cast::<Foo>();
unsafe { Box::from_raw(foo_ptr) }
};
assert_eq!(*foo, Foo { x: b'h', y: b'i' });
}