Passing mutable objects across ffi and back

Let's say I have a rust object, and I want to pass ownership of that object outside of rust. I want to then allow the external interface to call back into a rust function that changes the object, and then eventually get back ownership in the rust layer.

I am thinking the following approach:

// this can be called multiple times when the c layer has ownership,
// but never twice at the same time
fn rust_function(ptr: *mut c_void) {
    let rust_object = Box::leak(Box::from_raw(ptr as *mut RustObject));
    // now we can preform operations on rust_object that modify it
}

fn main() {
    let rust_object_ptr = Box::into_raw(Box::new(RustObject::new()));

    // calls c code that will call back into rust using `rust_function`. 
    preform_external_operations(rust_object_ptr as *mut c_void, rust_function);

    // now i have ownership of my rust object back
    let rust_object = Box::from(rust_object_ptr);
}

Looking for clarification if this is sane, and if there might be a better way. Thanks in advance!

Since managing the lifetime of RustObject is fully on Rust side (you never actually give up ownership – your main function is destroying the object, not C code), there's no need to box anything at all. Also, I think that all pointers (and references) have compatible ABI, so rust_function can just take &mut RustObject.

extern "C" fn rust_function(ptr: &mut RustObject) {
    // now we can preform operations on rust_object that modify it
}

fn main() {
    let mut rust_object = RustObject::new();

    // calls c code that will call back into rust using `rust_function`. 
    preform_external_operations(
        (&mut rust_object) as *mut RustObject as *mut c_void,
        rust_function
    );
}

Please also note that you need extern "C" declaration on all C-callable functions, because there's no guarantee that rust's calling convention is the same as C's.

2 Likes

Thanks, ill try that out.

Please also note that you need extern "C" declaration on all C-callable functions, because there’s no guarantee that rust’s calling convention is the same as C’s.

Yup! I was trying to simplify the example (the library has it set up so that you can pass a safe funtion, and the extern "C" fn calls the safe function) :slight_smile:, thanks though!

This seems to be true only for direct references to objects and not for references to trait objects (since the latter are fat pointers).

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.