extern "C" {
pub fn exec(f: Option<unsafe extern "C" fn(f: *const std::ffi::c_void)>,
i: *const std::ffi::c_void);
}
#[derive(Default)]
struct Foo {
// whatever
}
extern "C" fn use_foo(f: *const std::ffi::c_void) {
let f = unsafe { &*(f as *const Foo) };
// use Foo...
}
fn main() {
let foo: Foo = Default::default(); // some Foo
unsafe { exec(Some(use_foo), &foo as *const _ as _) };
}
Since the pointer dereference &*(f as *const Foo) is done on a f (as a valid Foo reference), I believe that calling the C function exec from Rust side in this case is always safe, for any data-layout of Foo.
always is ambiguous: the pattern used in main is indeed always safe, provided Foo is Sized.
However, both use_foo and exec are not "always safe", on the contrary, both require that f be a valid reference to a Foo.
That's why, for instance, your use_foo function should be declared unsafe.
Finally, as a safeguard, instead of
unsafe
extern "C"
fn use_foo (f: *const ::std::ffi::c_void)
{
let f: &Foo = &*(f as *const Foo);
// use Foo...
}
you should use either:
unsafe
extern "C"
fn use_foo (f: *const ::std::ffi::c_void)
{
// Check against NULL input
let f: &Foo =
(f as *const Foo)
.as_ref()
.unwrap_or_else(|| ::std::process::abort())
// use Foo...
}
or, if if you know you will be using that pattern anyways:
extern "C"
fn use_foo (f: Option<&'_ Foo>)
where
Foo : Sized,
{
// Check against NULL input
let f: &Foo = f.unwrap_or_else(|| ::std::process::abort())
// use Foo...
}
This has the advantage of no longer requiring the unsafe annotation, since Rust will then only be able to call it with None or Some valid reference to a Foo
but, for your example, this will give use_foo a different function pointer type than the one expected by exec, so you should use the previous pattern instead.