How to safely create an opaque struct and then free it over the FFI boundary?

Viewed 361

I am using cbindgen to generate C bindings for a small Rust crate that implements the ULID specification. To avoid leaking information, I am generating an opaque struct ulid_ctx and returning a pointer to that context object when it is first created. I'm struggling a little bit with reconciling Rust's ownership semantics and C's laissez-faire approach to memory.

#[allow(non_camel_case_types)]
pub struct ulid_ctx {
    seed: u32,
}
#[no_mangle]
pub extern "C" fn ulid_create(seed: u32) -> *mut ulid_ctx {
    let ctx = ulid_ctx { seed: s };
    Box::leak(Box::new(ctx))
}

#[no_mangle]
pub unsafe extern "C" fn ulid_ctx_destroy(ctx: *mut ulid_ctx) {
    Box::from_raw(ctx);
}

Two questions:

  1. Does Box::leak(Box::new(ctx)) correctly allocate a ctx value on the heap and then inform Rust that the function no longer owns it?
  2. Will Box::from_raw(ctx); re-create a Box and then immediately drop it, thereby freeing the memory?

Although it's not a lot of data (32 bits), I would like to avoid creating a memory leak if possible.

1 Answers

Does Box::leak(Box::new(ctx)) correctly allocate a ctx value on the heap and then inform Rust that the function no longer owns it?

Indeed, as the name says it leaks the data, so it will not be dropped when going out of scope. As per @user4815162342 comment, consider using Box::into_raw instead.

Will Box::from_raw(ctx); re-create a Box and then immediately drop it, thereby freeing the memory?

Also true, will build a Box then will be dropped. As a note, it may be nice to make the drop explicit.

#[no_mangle]
pub unsafe extern "C" fn ulid_ctx_destroy(ctx: *mut ulid_ctx) {
    drop(unsafe { Box::from_raw(ctx) })
}
Related