I'm looking for opinions and advice on the best way to cast "size of buffer" inputs required by ffi calls.
given a function of the shape:
pub unsafe fn DeviceIoControl(
hdevice: HANDLE,
dwiocontrolcode: u32,
lpinbuffer: *const core::ffi::c_void,
ninbuffersize: u32, // size of lpinbuffer
lpoutbuffer: *mut core::ffi::c_void,
noutbuffersize: u32, // size of lpoutbuffer
lpbytesreturned: *mut u32,
lpoverlapped: *mut OVERLAPPED,
)
I'm trying to decide between 2 fundamental approaches for npinbuffersize & npoutbuffersize
Option 1: const { size_of::<BUFFERTYPE>().strict_cast() }
Advantages I see:
- compile time validation. Compilation failure is better than runtime panic (or UB)
Disadvantages I see:
- risk: if
BUFFERTYPEchanges this line also needs to change - cross-compilation - risky pattern? Is this a possible footgun when used to cast to
usize, so while safe in the above situation which is castingusize as u32, the pattern would be dangerous if ever used in places where the resulting type isusize? - cross-compilation - does
const { size_of::<T> }use the target or host size ofTor does the addition ofconstbring in a footgun which I didn't notice in the docs? [Edit: found the (only?) reference, which confirms "The interpretation happens in the environment of the compilation target and not the host" - so this appears safe]
Option 2: size_of_val(lpinbuffer_val) as u32
Advantages I see:
- Guaranteed to use runtime size
- No experimental feature usage
- idiomatic
Disadvantages I see:
- Runtime panic is better and UB, but not as good as compilation error
- Won't panic on release-build. Needs safety comment & consideration of overflow/saturation risk.
Alternatives / Variations
- use
try_into()and handle error in code - While this is how I'd work in higher-level code it feels noisy & out of place during an ffi call in an unsafe block - non-
constoption 1 size_of_val().strict_cast()- guaranteed runtime panic, better than UB, but not as good as compile error (IMHO).- Re-reading the docs for
size_of_val()I see it is alsoconst, so maybe wrapping this inconstis the best option ... (pending cross-compilation footguns) ??? [EDIT: now I remember why I didn't do this - the buffer isn't const, so it won't compile)