I've already asked this on reddit but I'm thinking maybe more people will be able to help me here. If it's innappopriate, please pardon me and delete this post
I'm doing a POC in my company (trying to make a shift in our technology) and I'm using bindgen to generate a Rust FFI from a massive in-house SDK.
TheStruct* s = (TheStruct*) malloc(sizeof(TheStruct) + length_of_data);
bindgen generate a [UCHAR; 1usize] as the field type for the Rust struct.
In Rust, how I'm suppose to allocate TheStruct with a length_of_data in order to put some data I got from a parameter:
pub fn send_data(code: u8, data: &[u8]) -> MyResult {
let mut cmd: TheStruct = TheStruct {
// easy fields
data: // how to I stuck my parameter `data` in this data field?
};
}
Some people on reddit suggest using std::alloc but someone warned about alignment.
Thanks for any insight!
EDIT: the bindgenerated struct has #[repr(C, packed)]
One question: the content of data will always be a char type (an array of them, surely) or it can change? Because in the latter case, you don't have a fixed data parameter, because you should have a proper padding depending on the alignment of that type.
Just another small note: if your original C struct is not packed, you should remove the repr(packed). On most of the platform you should not get any padding (except for the situation I was writing before), but it is better to have a perfect consistency between the C and the Rust struct.
Oh, last thing: I don't know if your SDK defines UCHAR, 'USHORT' (and so on) in term of uint8_t, uint16_t (...). If not so, remember that using u8, u16 (...) in Rust can lead to undefined behaviour on some esoteric platforms, because the size of C unsigned char, unsigned short can be different from what you (and me, and probably most of the people) normally expect them to be.
So, going back to the original problem, what you expect to be stored on data?
Use mem::zeroed() as the initialiser if you don't want to be bothered specifying all fields (e. g. when C has an init method).
Use Box::new(obj) to allocate memory for the struct, and then as_ptr() to get a temporary pointer to it (C borrows it) or Box::into_raw() to get a permanent pointer.
You can’t use Box::new to alloc because the struct size isn’t actually the required size - it needs to overallocate to get the dynamic size accounted for.
This would likely work better with custom DST support, alas that’s not available.
@vitalyd do you know if at least it is possible to unsafe impl !Sized for T ? Even if we still don't have custom DST, at least it will make the compiler complain if you try to use the struct on the stack...
Right, DST are a bit hellish most of the time. I ended up needing heterogeneous DST data in C++, and I am using a constexpr nightmare of structures to auto-generate the correct exception safe implementation (and the obvious unit tests to check if everything worked flawlessly ).
I know that DST are not a priority for Rust at to date, but I hope to see something in the future, in order to have some safe abstractions for them with zero overhead.