The most direct version would be this:
use libc::c_char;
use std::ffi::CString;
use std::mem;
#[no_mangle]
pub extern fn query() -> *mut c_char {
let s = CString::new("Hello!").unwrap();
s.into_raw()
}
Here we return a pointer to a zero-terminated sequence of char
s which can be passed to Python's c_char_p
. You can't return just CString
because it is Rust structure which is not supposed to be used in C code directly - it wraps Vec<u8>
and actually consists of three pointer-sized integers. It is not compatible with C's char*
directly. We need to obtain a raw pointer out of it. CString::into_raw()
method does this - it consumes the CString
by value, "forgets" it so its allocation won't be destroyed, and returns a *mut c_char
pointer to the beginning of the array.
However, this way the string will be leaked because we forget its allocation on the Rust side, and it is never going to get freed. I don't know Python's FFI enough, but the most direct way to fix this problem is to create two functions, one for producing the data and one for freeing it. Then you need to free the data from Python side by calling this freeing function:
// above function
#[no_mangle]
pub extern fn query() -> *mut c_char { ... }
#[no_mangle]
pub extern fn free_query(c: *mut c_char) {
// convert the pointer back to `CString`
// it will be automatically dropped immediately
unsafe { CString::from_raw(c); }
}
CString::from_raw()
method accepts a *mut c_char
pointer and creates a CString
instance out of it, computing the length of the underlying zero-terminated string in the process. This operation implies ownership transfer, so the resulting CString
value will own the allocation, and when it is dropped, the allocation gets freed. This is exactly what we want.