Transmuting u8 buffer to struct in Rust

后端 未结 3 1330
爱一瞬间的悲伤
爱一瞬间的悲伤 2020-12-31 12:22

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 trie

相关标签:
3条回答
  • 2020-12-31 12:44

    You can use methods on raw pointers and functions in std::ptr to directly read/write objects in place.

    • std::ptr::read
    • std::ptr::read_unaligned
    • std::ptr::write
    • std::ptr::write_unaligned

    In your case:

    fn main() {
        let v: Vec<u8> = vec![1, 2, 3];
        let s: MyStruct = unsafe { std::ptr::read(v.as_ptr() as *const _) };
        println!("here is the struct: {:?}", s);
    }
    

    I would encourage you to wrap this in a reusable method and perform a length check on the source buffer before attempting the read.

    0 讨论(0)
  • 2020-12-31 12:45

    I gave up on the transmute stuff. *mut (raw pointers) in Rust are pretty similar to C pointers, so this was easy:

    #[repr(C, packed)] // necessary
    #[derive(Debug, Copy, Clone)] // not necessary
    struct MyStruct {
        foo: u16,
        bar: u8,
    }
    
    fn main() {
        let v: Vec<u8> = vec![1, 2, 3];
        let buffer = v.as_slice();
        let mut s_safe: Option<&MyStruct> = None;
        let c_buf = buffer.as_ptr();
        let s = c_buf as *mut MyStruct;
        unsafe {
            let ref s2 = *s;
            s_safe = Some(s2);
        }
        println!("here is the struct: {:?}", s_safe.unwrap());
    }
    

    The unsafe tag there is no joke, but the way I'm using this, I know my buffer is filled and take the proper precautions involving endianness later on.

    0 讨论(0)
  • 2020-12-31 12:53

    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:

    • How to read a struct from a file in Rust?
    • Can I take a byte array and deserialize it into a struct?
    0 讨论(0)
提交回复
热议问题