How to C - FFI with char** as output parameter?


I am trying to create a FFI for follwowing C-function:

int32_t get_string(char** string)

The function get_string allocates the memory. So only an address is passed like:

char* buffer;
int32_t ret = get_string(&buffer);

How can this achieved in Rust? I read MaybeUninit would be the right thing to use. But how?
I tried something like this:

let mut value = std::mem::MaybeUninit::<libc::c_char>::uninit();
let ret = get_string(&mut value.as_mut_ptr());

At least the compiler isn't complaining. But I fear that has nothing to say because of the unsafe.

My questions:

  1. How I pass the char** from Rust correctly?
  2. How can I convert the output value to a String?


When you pass &mut value.as_mut_ptr(), you're passing a reference to the temporary pointer. The FFI may set that to its allocated memory, but you have no way to see that update in your temporary pointer after it returns. It won't be pointing to your value anymore.

You could use an uninitialized pointer:

let mut buffer = MaybeUninit::<*mut libc::c_char>::uninit();
let ret = get_string(buffer.as_mut_ptr());

But it's also easy to just initialize it as null:

let mut buffer: *mut libc::c_char = std::ptr::null_mut();
let ret = get_string(&mut buffer);

The first version has the issue that if the function does not write into the argument, you get an uninitialized pointer.
Given essentially zero cost for the second version, I don't think the first version should ever be considered.

You can create a CStr from the pointer via CStr::from_ptr and then to_str.
Then convert that &str into a String and free the original pointer via the appropriate function call (probably free in libc - Rust)

Thanks to everyone. I'll take the pointer approach.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.