FFI: java call a rust dynamic library

I’m new to rust, and I have a java application call a rust dynamic library (cdylib) via jnr-ffi(https://github.com/jnr/jnr-ffi/).

Java code:

public interface LibHello {
    String hello();
}

LibHello lib = jnr.ffi.LibraryLoader.create(LibHello.class).load("hello");
System.out.println(lib.hello());

Rust code:

use std::ffi::CString;
use std::os::raw::c_char;

#[no_mangle]
pub extern fn hello() -> *mut c_char {
    let s = CString::new("hello").unwrap();
    s.into_raw()
}

And crate-type = [“cdylib”]

It just works fine, but I’m not sure if there is a memory leak, because CString.from_raw is never called to deallocate the memory, according to the doc here https://doc.rust-lang.org/std/ffi/struct.CString.html#method.into_raw

The pointer which this function returns must be returned to Rust and reconstituted using from_raw to be properly deallocated. Specifically, one should not use the standard C free() function to deallocate this string.

Failure to call from_raw will lead to a memory leak.

Is it a correct way or any better way to do this? thanks!

Try check this jni crate. To properly run destructor for object which is passed to JVM you should interact with its GC, and the easiest workaround is just create java String from rust side with desired contents and pass it.

Thanks, yes, I believe JNI can workaround this issue, JNI will require much more code than JNR-FFI, and JNR-FFI is pretty straightforward to do bindings to native libraries without writing JNI code at all.

Find a better way for this issue, return pointer to the string in java and provide a free_string method.

Java code:

public interface LibHello {
   jnr.ffi.Pointer hello();
   void free_string(Pointer ptr);
}

LibHello lib = jnr.ffi.LibraryLoader.create(LibHello.class).load("hello");
jnr.ffi.Pointer ptr = lib.hello();
String str = ptr.getString(0);
lib.free_string(ptr);  //release after use

Rust code:

#[no_mangle]
pub extern fn free_string(ptr: *mut c_char) {
    unsafe {
        if !ptr.is_null() {
            // retake pointer to free memory
            let _ = CString::from_raw(ptr);
        }
    }
}