Why does this type cast work?

Hello all. Recently I decided to try out rust to complete the other half of a simple portable executable loader I was working because Rust sounds appealing.

In C, I would cast the required structs from a pointer to parse the exe, as so:

PIMAGE_DOS_HEADER pdos = (PIMAGE_DOS_HEADER)imagebase;

I was looking for the proper way to do such things in rust, and found some code online that I tried to implement quickly to check for functionality. It worked, and the struct was filled with the required data, but I really am not sure why a certain line functions.

unsafe {
        let struct_slice = std::slice::from_raw_parts_mut(&mut dos as *mut _ as *mut u8, 512);
        stubsl.read_exact(struct_slice).unwrap();
    }

The "&mut dos as *mut _ as *mut" is pretty confusing. I am unsure why it enables the arbitrary pointer type cast. Can anyone explain why the underscore cast lets the cast happen?

Also, if anyone has any pointers on the proper/safe way to parse binary formats in Rust then I would be very appreciative of any advice.

You coerce your mutable reference to dos to a raw pointer to the type of dos and then coerce this raw pointer to a raw pointer pointing to a byte. The _ is just a placeholder for the compiler to infer the type of the first raw pointer in your coercion chain points to from &mut dos. Direct coercion from &mut T to *mut u8 is not supported by the compiler (read more about supported coercions here), so you got the coercion chain of &mut T -> *mut T -> *mut u8.

You can replace the _ placeholder with the type of dos to make the coercion more readable, i.e.:

struct T;

fn main() {
    let mut dos: T = T;
    
    unsafe {
        let struct_slice = std::slice::from_raw_parts_mut(&mut dos as *mut T as *mut u8, 512);
    }
}
1 Like

I recommend that you try out the zerocopy crate.

1 Like

_ in type position means "infer". While a regular reference can't be directly cast to a raw pointer of mismatching type, raw pointers can. So &mut dos as *mut _ converts from a reference to a raw pointer of the matching referent type, and then as *mut u8 performs the actual type conversion (from whatever the original pointee type was to u8).

1 Like

I see. Thanks for clearing this up for me!

1 Like

I'll give that a shot. Thanks for the reply.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.