viraptor
viraptor

Reputation: 34155

Getting raw bytes from packed struct

I've got a struct which looks like this:

#[repr(packed)]
struct Header {
    some: u8,
    thing: u8,
}

How can I get raw bytes from it, that I could use for either a C library or socket interaction?

I was hoping to use transmute to solve the problem, but unfortunately this doesn't work:

let header = Header {....}
let header_bytes: &[u8] = unsafe { mem::transmute(header) };
let result = self.socket.write(header_bytes);

This fails with

error: transmute called on types with different sizes: &Header (64 bits) to &[u8] (128 bits)

Upvotes: 4

Views: 7271

Answers (2)

Vladimir Matveev
Vladimir Matveev

Reputation: 127811

Edit: updated for Rust 1.x.

You can't transmute arbitrary thing to arbitrary thing (like Header to &[u8]) because transmute() is akin to reinterpret_cast in C++: it literally reinterprets the bytes which its argument consists of, and in order for this to be successful the source and the target type should have the same size. But even if Header and &[u8] had the same size, it wouldn't make sense to convert between them: Header is an actual value while &[u8] is a pointer with a size of data behind that pointer.

In order to interpret a piece of data as a slice of bytes you need to perform three steps: obtain a raw pointer to the data, convert it to a pointer to u8 and then convert that to a slice of u8. This means enhancing the raw pointer value with the length, which in this case is equal to the structure size. The last step can easily be done with std::slice::from_raw_parts():

use std::slice;
use std::mem;

let p: *const Header = &header;     // the same operator is used as with references
let p: *const u8 = p as *const u8;  // convert between pointer types
let s: &[u8] = unsafe { 
    slice::from_raw_parts(p, mem::size_of::<Header>())
};

However, doing this in most cases is not a good idea. Even though the struct is marked as packed, it still leaves the problem with the byte order, so at the very least the code which does this won't be portable (or rather, the data it serializes in such form may not be recoverable by the same program compiled for another architecture).

Upvotes: 10

bluss
bluss

Reputation: 13772

You can make any object into a byte slice with a function like this:

/// Safe to use with any wholly initialized memory `ptr`
unsafe fn raw_byte_repr<'a, T>(ptr: &'a T) -> &'a [u8]
{
    std::mem::transmute(std::raw::Slice{
        data: ptr as *const _ as *const u8,
        len: std::mem::size_of::<T>(),
    })
}

Run in Rust playpen.

I don't know the full safety analysis, but as long as you don't access any uninit values it should be fine.

Upvotes: 0

Related Questions