Being Sized
is a property of the type, not of any particular value. [u8]
, and [T]
in general is unsized. You can fallibly try to convert to a sized type, however.
let data: [u8; size_of::<Header>()] = buffer[0..size_of::<Header>()].try_into().unwrap();
try_into
usually returns a result, but since we know the size is correct, we can simply unwrap
it. (playground)
// The trait `TryInto` isn't in the prelude,
// so we have to import it to use its methods.
use std::convert::TryInto;
use std::mem::{size_of, transmute};
#[derive(Debug)]
#[repr(C)]
struct Header {
// some file header
magic: u32,
data1_len: u32,
data2_len: u32,
}
fn main() {
let buffer: Vec<u8> = vec![
1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0,
1, 2, 3, 4, 5, 6, 7, 8, 9, 0,
]; // whatever
let header: Header = {
let data: [u8; size_of::<Header>()] = buffer[0..size_of::<Header>()].try_into().unwrap();
unsafe { transmute(data) }
};
}
Note: this step only works because [T; N]
implements Copy
if T
implements Copy
. We're copying the data out of the vector. If this isn't what you want, you'll need to remove the data from the vector using Vec::splice
or similar. They can't both own that data.
You might also consider just getting a reference to a Header
, which should actually work here without copying anything or modifying the vector. I'd avoid the wild unsafety of transmute
and use the (slightly less unsafe) casting of raw pointers.
Unfortunately, the obvious thing to do here doesn't work since the alignment of a Header
is 4 bytes, but the alignment of [u8]
is only one byte. Pointer casting from &[u8]
to &Header
may cause an alignment fault. The reason that wasn't a problem with [u8; _]
to Header
was that transmute
takes the array by value and returns a properly aligned value. See pull request #38670.
One way around this is to make Header
repr(C, packed)
, which forces its alignment to be to one byte. This might make things a little slower in general (but remember: benchmark first!). I'm fairly certain this causes more problems than it's worth due to unsoundness around references to packed
structs, so be careful with this. (playground)
use std::mem::size_of;
// We can't derive anything unless we also derive Copy
// one of the downsides of this approach
#[repr(C, packed)]
struct Header {
// some file header
magic: u32,
data1_len: u32,
data2_len: u32,
}
fn main() {
let buffer: Vec<u8> = vec![
1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0,
1, 2, 3, 4, 5, 6, 7, 8, 9, 0,
]; // whatever
let header: &Header = unsafe { &*(buffer[0..size_of::<Header>()].as_ptr() as *const Header) };
}