Yes, it is, or more precisely, it ought to be valid. (Except that you have a type error down in the call, because if the function is declared to take Box<T>
, then you should pass the box directly instead of calling into_raw()
). Box<T>
does NOT change the FFI-safety of its contained value type. It neither removes nor adds FFI-safety.
Link to the relevant URLO thread, and to the docs that explicitly state:
So long as T: Sized
, a Box<T>
is guaranteed to be represented as a single pointer and is also ABI-compatible with C pointers (i.e. the C type T*
)
And to the announcement of Rust 1.41 which also explicitly says:
Starting with Rust 1.41.0, we have declared that a Box<T>
, where T: Sized
is now ABI compatible with the C language's pointer ( T*
) types. So if you have an extern "C"
Rust function, called from C, your Rust function can now use Box<T>
, for some specific T
, while using T*
in C for the corresponding function.
Except that the other direction (Rust calling into C) comes with a warning, whereby a bug in LLVM may still cause UB in valid code, so they do indeed advise programmers to mirror the C types as precisely as possible (using raw pointers in this case) as a best practice. But unless you hit this LLVM bug, passing a Box<T>
should work in theory, it should be equivalent with transferring ownership and passing a raw pointer, and once the LLVM bug is fixed, it will also be reliable in practice.
As to why you are getting the warning: it's an unavoidable false positive, no matter which approach you choose, the Box
or the raw pointer. The compiler can't possibly know whether you will actually dereference the pointer on the C side, since it doesn't, it can't, analyze your C code (and for all intents and purposes, you might as well be linking to an opaque library without source).
So it's merely being conservative, emitting the warning anyway, because it's better to get a false positive warning (that you can suppress explicitly if you promise you won't dereference the pointer from C) than it is to get a false negative and silently cause UB if you simply forgot to add the #[repr(C)]
on your struct
that you do however intend to dereference from C.
The safest way to solve this issue is, however, to use an opaque pointer instead. Take void *
on the C side, cast to *mut c_void
on the Rust side, and this guarantees (quasi, modulo arcane C hacks) that you won't be able to dereference the pointer on the C side, and this makes the warning go away without needing to slap an #[allow(improper_ctypes)]
on your struct
declaration.
(Note that this still doesn't allow you to violate the "single ownership" and "no mutable aliasing" rules, so you will have to enforce those on your own, paying special attention to the C code and the interaction between the C and the Rust parts of the code.)