Search code examples
rustdata-structuresunsafe

How to transmute a u8 buffer to struct in Rust?


I have a byte buffer of unknown size, and I want to create a local struct variable pointing to the memory of the beginning of the buffer. Following what I'd do in C, I tried a lot of different things in Rust and kept getting errors. This is my latest attempt:

use std::mem::{size_of, transmute};

#[repr(C, packed)]
struct MyStruct {
    foo: u16,
    bar: u8,
}

fn main() {
    let v: Vec<u8> = vec![1, 2, 3];
    let buffer = v.as_slice();
    let s: MyStruct = unsafe { transmute(buffer[..size_of::<MyStruct>()]) };
}

I get an error:

error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
   --> src/main.rs:12:42
    |
12  |     let s: MyStruct = unsafe { transmute(buffer[..size_of::<MyStruct>()]) };
    |                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
    |
    = help: the trait `std::marker::Sized` is not implemented for `[u8]`
    = note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>

Solution

  • If you don't want to copy the data to the struct but instead leave it in place, you can use slice::align_to. This creates a &MyStruct instead:

    #[repr(C, packed)]
    #[derive(Debug, Copy, Clone)]
    struct MyStruct {
        foo: u16,
        bar: u8,
    }
    
    fn main() {
        let v = vec![1u8, 2, 3];
    
        // I copied this code from Stack Overflow
        // without understanding why this case is safe.
        let (head, body, _tail) = unsafe { v.align_to::<MyStruct>() };
        assert!(head.is_empty(), "Data was not aligned");
        let my_struct = &body[0];
    
        println!("{:?}", my_struct);
    }
    

    Here, it's safe to use align_to to transmute some bytes to MyStruct because we've used repr(C, packed) and all of the types in MyStruct can be any arbitrary bytes.

    See also: