Skip to main content
edited tags; edited title
Link
200_success
  • 145.6k
  • 22
  • 191
  • 481

Exposing a simple struct using Rust FFI and memory management

Source Link
Mongus Pong
  • 776
  • 3
  • 10

Rust FFI and memory management

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); 
}