flavius
December 19, 2018, 8:22pm
1
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.
flavius
December 19, 2018, 8:30pm
3
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.
flavius
December 19, 2018, 8:36pm
5
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
flavius
December 19, 2018, 8:41pm
7
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.