How to convert ‘struct’ to ‘&[u8]’?

A correctly sized struct as zero-copied bytes can be done using stdlib and a generic function.

In the example below there there is a reusable function called any_as_u8_slice instead of convert_struct, since this is a utility to wrap cast and slice creation.

Note that the question asks about converting, this example creates a read-only slice, so has the advantage of not needing to copy the memory.

Heres a working example based on the question:

unsafe fn any_as_u8_slice<T: Sized>(p: &T) -> &[u8] {
    ::std::slice::from_raw_parts(
        (p as *const T) as *const u8,
        ::std::mem::size_of::<T>(),
    )
}

fn main() {
    struct MyStruct {
        id: u8,
        data: [u8; 1024],
    }
    let my_struct = MyStruct { id: 0, data: [1; 1024] };
    let bytes: &[u8] = unsafe { any_as_u8_slice(&my_struct) };
    // tcp_stream.write(bytes);
    println!("{:?}", bytes);
}

Note 1) even though 3rd party crates might be better in some cases, this is such a primitive operation that its useful to know how to do in Rust.

Note 2) at time of writing (Rust 1.15), there is no support for const functions. Once there is, it will be possible to cast into a fixed sized array instead of a slice.

Note 3) the any_as_u8_slice function is marked unsafe because any padding bytes in the struct may be uninitialized memory (giving undefined behavior).
If there were a way to ensure input arguments used only structs which were #[repr(packed)], then it could be safe.

Otherwise the function is fairly safe since it prevents buffer over-run since the output is read-only, fixed number of bytes, and its lifetime is bound to the input.
If you wanted a version that returned a &mut [u8], that would be quite dangerous since modifying could easily create inconsistent/corrupt data.

Leave a Comment