Best practice for calling rust functions from C


I’m writing a wrapper for rust-url and I’m noticing there aren’t many resources about this, certainly not a best practice guide.

People have suggested html5ever as an example.

So the API would look something like this:

pub type funky_ptr = *const ();

pub unsafe extern "C" fn create_funky() -> funky_ptr {
  let funky: Box<i32> = Box::new(42);
  mem::transmute(funky) // returns a pointer to the box

pub unsafe extern "C" fn free_funky(ptr: funky_ptr) {
  let funky: Box<i32> = mem::transmute(ptr);
  // memory gets released when the box goes out of scope
  // drop(funky);

pub unsafe extern "C" fn use_funky(ptr: funky_ptr) {
  let funky: &i32 = mem::transmute(ptr);
  // does not free the memory

Even when returning CStrings, freeing the memory must be done by calling back into Rust, because the allocator may be different from the one C is using.

Please post any feedback you may have.


You can use Box::from_raw and std::boxed::into_raw as more type-checked conversions than transmute.

I’m not sure if transmuting a pointer to Box into a reference is officially defined behavior; you’d need some lifetime for that. Alternatively, it’s possible to explicitly “lose” the box at the end of use_funky with into_raw or std::mem::forget.

IIRC there were lints about not using the libc types in FFI, so funky_ptr might better be defined as *const libc::c_void.


Pardon my ignorance, but it appears to be really difficult to find clear-cut facts on this very subject. If I have existing C/C++ codebase and I want to rewrite portion of it in Rust (make it a library?), would I lose key features of the Rust language, such as better memory management?


In short: No.

A longer answer would require more information about what you are planning to do. You of course also need to use Rust types in your Rust code. E.g. wrap array pointers in slices instead of working with raw pointers etc…


Note that it is currently highly unsafe to call Rust from C: if an unwind happens, you are basically screwed. See an issue and an RFC PR about this.