Get generic value as bytes

I'm working on a generic function which is supposed to take any type (generic T) and write it to a file.

For this, I need to cast the value to &[u8].

The things I've read so far about transmute make me careful, so: what is the safest way of writing any value's bytes to a file?

Non-working code:

    fn sync_write<T>(&mut self, val: &T)
where
    T: ?Sized
{
    let slice = unsafe {
        //std::slice::from_raw_parts(val as *const u8, std::mem::size_of_val(val))
        //std::mem::transmute<&T, &[u8]>(val, std::mem::size_of_val(val))
    };
    self.file.write(slice);
    self.cursor = self.file.seek(std::io::SeekFrom::Current(0)).unwrap();
}

This is not going to work with any type that involves any amount of pointer indirection (e.g. Vec, Box, HashMap, ...).

You should use a serialization format like bincode, JSON, cbor, etc.

Think of it as if I'm implementing my own serialization scheme.

I refuse to believe that there's absolutely no way of getting the bytes of a block of memory, even if that block of memory contains yet another address (pointer).

You've already demonstrated a way of getting the bytes of a block of memory in the initial post, unless I'm missing something?

Be careful when accessing the buffer - padding bytes have undefined values, and use of them can easily cause UB.

Both lines generate an error each which I don't know how to tackle.

fn sync_write<T>(&mut self, val: &T) {
    let slice = unsafe {
        std::slice::from_raw_parts(val as *const T as *const u8, std::mem::size_of_val(val))
    };
    self.file.write(slice);
    self.cursor = self.file.seek(std::io::SeekFrom::Current(0)).unwrap();
}
3 Likes

Oh ok, I get it, although the error message would not make me think of this repeated casting. Thanks.

In c like languages with pointers, which I used very little of but still played around with, what needs to be done is interpreting the *T as *char (Which are bytes in a sense), but usually in rust you have a reference, and this cannot be casted to anything other than the following

let b = &1;
let c = b as *const usize;

(or *mut usize) and this leads to a double cast.