I spent a little while yesterday figuring out how to convert between rust references and C style FFI void*
pointers, and I thought it worth writing a few notes on my experiences. There were a few wrinkles which tripped me up.
First of all, I struggled to find a concise summary of how to do this. Eventually I ended up with two simple wrapper functions which do the job:
unsafe fn voidp_to_ref<'a, T>(p: *const c_void) -> &'a T
{
unsafe { &*(p as *const T) }
}
fn ref_to_voidp<T>(r: &T) -> *const c_void
{
r as *const T as *const c_void
}
My observations in getting there, in no particular order.
-
Actually one of the
unsafe
keywords needs to go, but I'm not sure which is the right one to lose. I find it odd that anunsafe
function automatically marks its entire implementation as unsafe, I'd rather focus my use ofunsafe
where it's important. -
Speaking of focusing
unsafe
, this threw me for a bit: I know that the dereference*(...)
is the truly unsafe part, so I wrote the following instead (and not wrapped inside a function, so the object lifetime was less managed):&unsafe{*(p as *const T)}
Turns out that this has completely different semantics (returning a reference to a copy of
*p
instead)! Wow. Are there other cases where the presence or absence ofunsafe
makes a semantic difference to code that otherwise compiles? Or is it just this particular&*
glyph that needs to be left unbroken? -
Finally, the double cast in
ref_to_voidp
was a surprise, though I can see why it's happening. The compiler messages didn't help me when I tried the direct castr as *const c_void
: the compiler just says "E0606 casting ... is invalid"; for quite a while I thought I was on completely the wrong track.
My main problem was probably finding my way around the documentation, but it's hard to find a concise summary of the bits I'm after at any particular moment!
By the way, what is the inferred lifetime 'a
from &*p
? I think I have no idea... unless it's simply "as long as required" (which I guess might as well be 'static
)!