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 CString
s 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.