I would like create a Rust FFI function that takes an out-parameter for arrays, analogous to this C function:
size_t arr_out_c(char ***out) {
char *msg = "the quick brown fox";
size_t STR_LEN = strlen(msg) +1;
size_t count = 3;
char** arr = calloc( count, sizeof(char*));
for (int i = 0; i < count; i++) {
arr[i] = calloc(STR_LEN, sizeof(char));
strncpy(arr[i], msg, STR_LEN * sizeof(char));
}
*out = arr;
return count;
}
Usage:
char** test;
size_t count = arr_out_c(&test);
for (int i = 0; i < count; i++) {
printf("item: %s", test[i]);
}
I've gotten close with this:
#[no_mangle]
pub extern fn arr_out_rust(strings_out: *mut *mut *mut ::std::os::raw::c_char) -> usize {
unsafe {
let s1 = ::std::ffi::CString::new("the quick brown fox").unwrap();
let s2 = ::std::ffi::CString::new("the quick brown fox").unwrap();
let mut strs = vec![s1, s2];
let len = strs.len();
let mut boxed_slice = strs.into_boxed_slice();
*strings_out = boxed_slice.as_mut_ptr() as *mut *mut ::std::os::raw::c_char;
mem::forget(boxed_slice);
len
}
}
It works for the first item in the array, attempting to access the 2nd item results in a SEGV. Perhaps the CStrings are not getting packed in a contiguous block of memory... or maybe the len data in slice is part of that memory?
Also, to free this memory, can I just call the C free function on it in C space, or do I need to send it back into Rust space and call drop?
char** test;
size_t count = arr_out_rust(&test);
for (int i = 0; i < count; i++) {
printf("item: %s", test[i]);
}
will print a single the quick brown fox, then crash.
While this question has some similarities to questions asking about returning strings to C, it's pretty unique in that it is asking about out-bound pointers in FFI. Marking it as a duplicate and closing it would go against the spirit of Stack Overflow as a repository of unique answers.
CStringacross the FFI boundary?CStringinstead of* const c_char. And yes, you always have to free Rust allocations back in Rust land. You will need to reconstitute theVecand then let it drop.