Ответ 1
Самая прямая версия будет следующей:
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()
}
Здесь мы возвращаем указатель на нулевую последовательность char
, которая может быть передана Python c_char_p
. Вы не можете вернуть только CString
, потому что это структура Rust, которая не должна использоваться в C-коде напрямую - она обертывает Vec<u8>
и фактически состоит из трех целых чисел с указателем. Он несовместим с C char*
напрямую. Нам нужно получить из него необработанный указатель. CString::into_raw()
метод делает это: он потребляет значение CString
по значению, "забывает" его, поэтому его распределение не будет уничтожено и возвращается a *mut c_char
указатель на начало массива.
Однако таким образом строка будет просочиться, потому что мы забываем ее выделение на стороне Rust, и она никогда не будет освобождена. Я не знаю Python FFI достаточно, но самый прямой способ исправить эту проблему - создать две функции: одну для создания данных и одну для ее освобождения. Затем вам нужно освободить данные со стороны Python, вызвав эту функцию освобождения:
// 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()
метод принимает указатель *mut c_char
и создает из него экземпляр CString
, вычисляя длину нулевого нуля -терминированная строка в этом процессе. Эта операция подразумевает передачу прав собственности, поэтому полученное значение CString
будет принадлежать распределению, а когда оно будет сброшено, распределение будет освобождено. Это именно то, что мы хотим.