I am trying to allocate a DST and I started noticing a problem. The allocate_zeroed
function returns a [u8]
, which is a fat pointer with the size of the whole struct. I am casting it to a fat pointer *mut DST
, which reinterprets the fat pointer size to be the data size. This is incorrect; I want the fat pointer size to represent the data size, not the size of the entire DST in bytes.
use std::alloc::{Allocator, Global, Layout};
#[repr(C)]
struct DST {
other_data: usize,
other_data2: usize,
other_data3: usize,
other_data4: usize,
data: [u8],
}
unsafe fn create(data_size: usize) {
let fat_ptr = Global
.allocate_zeroed(Layout::from_size_align((size_of::<usize>() * 4) + data_size, 8).unwrap())
.unwrap()
.as_ptr() as *mut DST;
assert_eq!((*fat_ptr).data.len(), data_size + size_of::<usize>() * 4); // Pass
assert_eq!((*fat_ptr).data.len(), data_size); // Failed
}
I want to change the fat pointer size (if that's possible) to data_size
. Or make the the fat pointer have the correct size.
The behaviour you are witnessing is documented in The Rust Reference section on type cast expressions, under Pointer-to-pointer cast (emphasis added):
*const T
/*mut T
can be cast to*const U
/*mut U
with the following behavior:
[ deletia ]
If
T
andU
are both unsized, the pointer is also returned unchanged. In particular, the metadata is preserved exactly.For instance, a cast from
*const [T]
to*const [U]
preserves the number of elements. Note that, as a consequence, such casts do not necessarily preserve the size of the pointer’s referent (e.g., casting*const [u16]
to*const [u8]
will result in a raw pointer which refers to an object of half the size of the original). The same holds forstr
and any compound type whose unsized tail is a slice type, such asstruct Foo(i32, [u8])
or(u64, Foo)
.
Instead you must first manually create a pointer with the correct metadata for your struct.
On stable Rust, this can be done using e.g. std::ptr::slice_from_raw_parts_mut
while on nightly Rust with the ptr_metadata
feature you can go more directly via std::ptr::NonNull::from_raw_parts_mut
.
Note also that your layout calculations are incorrect, as you are ignoring padding (both between the fields and of the resulting struct).