I am trying to expose a simple struct via FFI. Is this the best way to do it?
I am not 100% sure about the get_text function. This is taking the string out of the struct and cloning it before returning a pointer to it. I suspect this is going to leak. Whats the best idiomatic way around this? Do I need to provide a free_string function? Is there a way to just return a pointer into the Rust string?
use std::mem;
use std::os::raw::{c_char, c_void};
use std::ffi;
#[derive(Debug)]
pub struct SomeData {
text : String,
number : usize,
}
#[no_mangle]
/// Make the structure. Box it to stick on the heap and return a pointer to it.
/// This absolves Rust from having to deal with the memory, so it is the callers
/// responsibility to free it by calling drop_data.
pub extern "C" fn make_some_data (text: *const c_char, number: usize) -> *mut c_void {
let cstr = unsafe {
assert!(!text.is_null());
ffi::CStr::from_ptr(text)
};
let data = cstr.to_str().ok().map(|utf8_str| {
Box::into_raw(Box::new(SomeData {
text: String::from(utf8_str),
number,
}))
}).unwrap();
data as *mut c_void
}
#[no_mangle]
/// Returns the text field of the struct.
pub extern "C" fn get_text(data: *mut c_void) -> *mut c_char {
let data = unsafe {
assert!(!data.is_null());
Box::from_raw(data as *mut SomeData)
};
let text = data.text.clone();
mem::forget(data);
ffi::CString::new(text).unwrap().into_raw()
}
#[no_mangle]
/// Returns the number field of the struct.
pub extern "C" fn get_number(data: *mut c_void) -> usize {
let data = unsafe {
assert!(!data.is_null());
Box::from_raw(data as *mut SomeData)
};
let number = data.number;
mem::forget(data);
number
}
#[no_mangle]
/// Frees the memory.
pub extern "C" fn drop_data (data: *mut c_void) {
mem::drop(data);
}