Alignment issue casting from &[u8] to &[u32] at compile time

I'm trying to read bytes from a binary file and cast the pointer to the slice to &[u32]. It works at runtime using the fs module:

let mut buf = Vec::new();

let file = File::open("slang.spv").unwrap();
let mut reader = BufReader::new(file);
reader.read_to_end(&mut buf).

let code = std::slice::from_raw_parts(buf.as_ptr() as *const u32, buf.len() / 4);

However at compile time:

let buf = include_bytes!("../../slang.spv");
let code = std::slice::from_raw_parts(buf.as_ptr() as *const u32, buf.len() / 4);

It panics with unsafe precondition(s) violated: slice::from_raw_parts requires the pointer to be aligned and non-null, and the total size of the slice not to exceed isize::MAX

There is no guarantee that include_bytes gives you something 4-aligned, so this is to be expected.

Put it in a static that you manually align (perhaps via a wrapper type with #[repr(align(4))]) if you want to use it in a type with a validity invariant of being 4-aligned.

There's also no guarantee that your buf will be either, so that's also technically unsound, though assuming your spv file isn't tiny most allocators will probably happen to give you something 4-aligned. To do it properly, you'd be better off making a Vec<u32>, then writing the file into that -- &mut [u32] -> &mut [u8] can be done infallibly -- so that you have the allocation aligned as you apparently need.

3 Likes

there's simple trick to align the included data, see

5 Likes