This should be well-defined and doesn't assume the fat pointer layout:
#[derive(Debug, Fatten)]
#[repr(C)] // repr for what I put in main, not required in general
struct Struct {
head: u8,
tail: [u8],
}
// derive(Fatten) expands to:
impl Struct {
fn fatten(data: *const u8, len: usize) -> *const Self {
// Requirements of slice::from_raw_parts.
assert!(!data.is_null());
assert!(len <= isize::max_value() as usize);
let slice = unsafe { core::slice::from_raw_parts(data as *const (), len) };
slice as *const [()] as *const Self
}
}
fn main() {
let array = [2u8, 3u8, 4u8, 5u8, 6u8];
let ptr = Struct::fatten(array.as_ptr(), 4);
println!("{:?}", unsafe { &*ptr });
}
If the caller's struct has anything other than a dynamically sized slice as the tail, for example tail: dyn ToString or tail: T where T: ?Sized, it correctly won't compile:
error[E0606]: casting `*const [()]` as `*const Struct` is invalid
--> src/main.rs:18:9
|
18 | slice as *const [()] as *const Self
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: vtable kinds may not match