A way to return stack-allocated #[repr(Rust)] types to C through FFI?

I want to return a stack-allocated (local-allocated) struct through FFI. Along these lines:

#[repr(C)]
pub struct OpaqueWrapper(String);

#[no_mangle]
pub extern "C" fn some_func() -> OpaqueWrapper {
    OpaqueWrapper("hello".to_string())
}

My main goal in wanting to do this is to avoid the allocation that comes with a Box. However, I have no way of making the internal wrapped types be #[repr(C)].

On the C side, I don’t need the C code to be able to see the structure of the data (in fact it’s better the structure is totally opaque to C), but it does need to have the right size. ie. sizeof() in C, to account for the storage of the Rust type.

Is there a recommended way to do this?

If not, it seems possible that a type could be created (ReprWrapper<T> seems like a good name) that acts as a placeholder (array of bytes, maybe) with the same size as the type, and has some (unsafe) conversion functions to get to and from the original T. Possibly a proc macro would be needed to make it work. But I don’t want to engineer a kludge if there’s already an established way of doing this.

Thank you.

Perhaps you could give the C code a raw pointer that it should write the value to, instead of actually returning it normally?

2 Likes

I don't quite understand your suggestion. Are you saying the C code allocates a buffer for the object (or reserves stack space), passes the pointer to Rust, and Rust writes the data into the pointer?

That would work except C somehow needs to know the size. Which would need to probably come from a struct in a header file. So I guess then the challenge becomes auto-generating header files with placeholder structs where the size reflects the size of the Rust types they wrap.

Am I understanding your suggestion?

I assumed that you're doing some sort of Rust -> C -> Rust call, in which case you can allocate the stack space on the outer Rust side.

Ah. No. In this use case, C is the outermost part of the call stack.

You could use cbindgen to generate header files.

1 Like