Is a C caller responsible of dropping a struct returned by a Rust callee?

The background is that I'm trying to write a lib in Rust, and to be called from C.

Say I have a struct, and it explicitly implemented Drop trait, which enables it release some resource (say a block of raw memory pointed by a *mut raw pointer) when itself is finalized. I have a function declared as pub extern "C", which enables me to call it from the C-side, and this function returns a value of the struct (so that not a pointer).

The question is that If the call in C side responsible of calling the drop function of that struct before this struct goes out of its scope?

3 Likes

It is. From C's perspective, that struct is just a hunk of bytes. There isn't anything special that will ask C to call Drop as it goes out of scope. Once you moved it out of Rust's purview, the automatic calling of Drop went away and you have to do it yourself at the appropriate time.

Here's an example I found of an FFI that returned pointers to structs and transmuted it back and forth from a raw pointer in order to allow rust to both allocate and deallocate the struct: Complex types with Rust’s FFI. Interop with object methods, structs… | by Jim Fleming | Jim Fleming | Medium

Edit: After a bit of googling it seems like most people return pointers (by returning a Box), not the struct itself, and leave it up to C to free it. You would probably want to explicitly expose the Drop function through FFI as well.

3 Likes

If C code has a ptr to the struct (allocated in Rust via a Box), then the canonical way to free it on the Rust side is to expose a fn that does Box::from_raw(ptr) inside and then letting the box drop, which calls the types drop() impl if it has one. So something like:

#[no_mangle]
pub unsafe extern fn my_type_free(ptr: *mut MyType) {
    // real code should handle ptr.is_null() case
    Box::from_raw(ptr);
}

So you don’t expose the drop impl directly.

4 Likes

rules of thumb: The component that allocated dynamic memory should free it as well (it might use a different allocator-implementation internally). This rule is crucial using DLLs on Windows, otherwise facing memory leaks or worse.

EDIT you should take the pattern vitalyd proposed.

1 Like