Yeah I just tripped over this myself. Suppose an FFI interface where library code calls into application code, supplying, in C terms, a callback pointer that may be NULL. You're supposed to call it at an appropriate point but only if it's not NULL.
void called_from_library(void (*callback)(void) /* , other arguments */)
{
// ...
if (callback) callback();
// ...
}
The most natural Rust implementation would use Option<fn()> as the callback argument:
fn called_from_library(callback: Option<fn()>, /* other arguments */) {
// ...
if let Some(callback) = callback { callback(); }
// ...
}
And that's perfectly fine if the library is also implemented in Rust. But if the library is C, then, as I read it, the null pointer optimization guarantee for Option does not quite promise that an Option<extern "C" fn()> parameter declaration is compatible with a C caller supplying a void (*)(void) actual argument. I think it's probably meant to, but there's a missing piece: for neither function nor object pointers does it actually say that transmute::<Option<&T>>(ptr::null<T>()) is guaranteed to be valid and to produce None, nor the equivalent in the opposite direction.
Given that, what I wound up writing instead was
#[no_mangle] unsafe extern "C"
fn called_from_library(callback_raw: *const ffi::c_void, /* other arguments */) {
// ...
if let Some(callback_ref) = callback_raw.as_ref() {
let callback_fn: extern "C" fn() = mem::transmute(callback_ref);
callback_fn()
}
// ...
}
I believe this would be sound even if void (*)(void) and Option<extern "C" fn()> weren't transmute-compatible (as long as extern "C" fn() and &c_void are transmute-compatible, of course). But it sure would be nice to be able to write something more like the all-Rust version. And having some kind of "raw function pointer" type in the language would help.
This is wholly independent of the earlier discussion of giving some chunks of machine code a lifetime shorter than 'static, though.