Pass pointer of pointer in rust to FFI for C

I have a function with such a signature and usage in C:

char* s;
read_string(&s); // signature is read_string(void* s), but this is how it should work (really...), because this function can be used in two different ways in C
std::string value = s;

So what's happening there:

  1. We create a pointer
  2. We pass the pointer of that pointer to the function
  3. The function allocates a new null-terminated string
  4. We assign that null-terminated string to std::string, which is the official, clear string type in C++

Please note that I'm simplifying this a lot. There's error-handling involved.

How can I do the same in rust? How can I pass the pointer of a pointer?

The furthest I reached was:

extern "C" {
    pub fn read_string(buf: *mut c_char);

let mut str_ptr: *mut c_char = std::ptr::null_mut();
read_string(&s); // this doesn't compile... of course, because of type-safety.

How can I achieve the same result I have in the example C++ code above?

Thanks in advance :smile:

Is read_string supposed to fill an existing buf allocation? Or set the buf pointer to a new allocation? If the latter, it should be declared read_string(buf: *mut *mut c_char).

You're right. The signature is wrong, but it still works somehow in C. The function can be called in two different states defined at run-time, and in one state it'll be used as void* buf and the other will be void** buf.

In other words, the signature from C is correct (but is semantically wrong)... and Rust, somehow, has to deal with it... that's my problem.

The function is actually H5Aread. An example of it can be found here.

C lets you pass any pointer to a void* -- if you have char**, the inner char* becomes void. This is what's happening in your original example calling read_string(&s).

You can represent that in Rust too, read_string(*mut c_void), but you'd have to explicitly cast the types away at the call site like read_string(&mut str_ptr as *mut *mut c_char as *mut c_void). They can both be inferred as *mut _ if you prefer, casting reference to pointer and then changing pointer type.

1 Like

I see. Thank you. I'll try this out and see if my tests blow up :smile:

Thank you very much, this works. I also confirmed with Valgrind that the cleanup was done correctly in a reproducible manner.