@quinedot I appreciate the ideas there, and I've since used Default
similarly to initialize the memory. It's wasteful, but it's wasteful one time per object in the scale of the program, when this is going to be a utility (reads a few things once, not frequent disk access), so it's OK to duplicate this.
As for the POD problem, is there a trait bound I could put on T
for this? I'd rather do so. I tried searching for such, but couldn't find one, but that could easily be "I'm inexperienced at Rust" rather than "it doesn't exist."
@kornel An interesting discussion on how/why to do this type of thing. I don't know the UB rules (very few rules in fact) which is part of the reason I asked what's "right/wrong" about what I'm doing rather than "it seems to work" for it.
Here's my modified version that does use Default
and functions so far with multiple POD types, but POD is not yet enforced (that I know of). Any help with how to do that at compile-time would be great.
const BIG_STRUCT_SIZE: usize = 140000;
#[derive(Clone, Copy, Debug)]
#[repr(packed(1))]
struct My_Big_C_Struct{
// Not implemented the sub-structures yet!
bulk: [u8; BIG_STRUCT_SIZE - mem::size_of::<u32>()],
crc: u32, // Every struct has a crc at the end
}
impl Default for My_Big_C_Struct{
fn default() -> Self {
Self {
bulk: [u8; BIG_STRUCT_SIZE - mem::size_of::<u32>()], // Note: I'd love to say "init to 0, the entire array" rather than specifying
crc: Default::default()
}
}
}
// Reads in the specified struct from the specified path. If there is not an exact match on size, returns an error
fn read_struct<T: Default, P: AsRef<Path>>(path: P) -> std::io::Result<Box<T>> {
let path = path.as_ref();
let struct_size = ::std::mem::size_of::<T>();
let num_bytes = fs::metadata(path)?.len() as usize;
if struct_size != num_bytes {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!(
"Length of file did not match structure specified. File length: {}, Expected: {}",
num_bytes, struct_size
),
));
}
let mut reader = BufReader::new(File::open(path)?);
let boxed_value = Box::new(T::default());
let ptr_from_box = Box::into_raw(boxed_value);
unsafe {
let buffer = slice::from_raw_parts_mut(ptr_from_box as *mut u8, num_bytes);
reader.read_exact(buffer)?; // does this cause the ptr_from_box to leak on failure?
Ok(Box::from_raw(ptr_from_box))
}
}
This enforces Default
on the type, being read in, and initializes the Box<T>
contents and uses Box::into_raw
as well to get the pointer. I have a concern that there's a leak, though I don't know for sure.
I respect those who have done this longer to find the UB here on POD types. It "seems to work" but that's not good enough for me. I don't like it when people do that, so I'm not going to have my first substantial foray into Rust start that way either!