When working with Foreign Function Interfaces (FFIs), I regularly see a double cast from reference-to-pointer-to-struct to pointer-to-pointer-to-void. For example, given an FFI-like function:
unsafe fn ffi(param: *mut *mut c_void) {}
The way to call this is:
struct foo;
let mut bar: *mut foo = ptr::null_mut();
unsafe { ffi(&mut bar as *mut *mut _ as *mut *mut c_void); }
Removing the intermediate cast yields this error:
error[E0606]: casting `&mut *mut foo` as `*mut *mut winapi::ctypes::c_void` is invalid
--> src\main.rs:36:18
|
36 | unsafe { ffi(&mut bar as *mut *mut c_void); }
|
I tried to get the compiler to tell me what the intermediate type is by forcing it into an obviously wrong type:
let mut bar: *mut foo = ptr::null_mut();
let mut test: u8 = &mut bar as *mut *mut _;
Which resulted in this error:
error[E0308]: mismatched types
--> src\main.rs:36:24
|
36 | let mut test: u8 = &mut bar as *mut *mut _;
| ^^^^^^^^^^^^^^^^^^^^^^^ expected u8, found *-ptr
|
= note: expected type `u8`
found type `*mut *mut _`
But *-ptr
doesn't seem to be an actual type that I can put in place of _
. Why is the intermediate as *mut *mut _
required, and what is the inferred type?
I found this question which is related (Working with c_void in an FFI) but it doesn't actually explain anything about the double cast.
If you have:
let mut bar: *mut foo = ptr::null_mut();
Then you take &mut bar
, the type is &mut *mut foo
. But you need *mut *mut foo
, so you can simply coerce it by doing &mut *mut foo as *mut *mut _
, where _
is inferred as foo
(try typing it explicitly: *mut *mut foo
). Once you have it as a raw pointer, then you are able to cast to *mut *mut c_void
.
So to recap, the double cast is necessary to first coerce from a reference to a raw pointer, then from a raw pointer cast to a c_void
, because you otherwise normally can't cast straight from a reference to a raw c_void
pointer.
Fully typed example:
let mut bar: *mut foo = std::ptr::null_mut();
let mut_ref: &mut *mut foo = &mut bar;
let raw_ptr: *mut *mut foo = mut_ref as *mut *mut _;
let void_cast: *mut *mut c_void = raw_ptr as *mut *mut c_void;
unsafe { ffi(void_cast); }
Playground
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With