我对Rust很新.我如何String从可以在Python中使用的Rust函数返回一个?
这是我的Rust实现:
use std::ffi::CString;
#[no_mangle]
pub extern fn query() -> CString {
let s = CString::new("Hello!").unwrap();
return s;
}
Run Code Online (Sandbox Code Playgroud)
以及调用它的Python代码:
from ctypes import cdll, c_char_p
lib = cdll.LoadLibrary("target/release/libtest.so")
result = lib.query()
print(c_char_p(result).value)
Run Code Online (Sandbox Code Playgroud)
它运行时出现分段错误.
编辑:使用下面的Vladimir Matveev的Rust代码,我能够使用我的python代码的更改:
from ctypes import *
lib = cdll.LoadLibrary("target/release/libtest.so")
lib.query.restype = c_char_p
result = lib.query()
print cast(result, c_char_p).value
lib.free_query(result)
Run Code Online (Sandbox Code Playgroud)
Vla*_*eev 12
最直接的版本是:
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()
}
Run Code Online (Sandbox Code Playgroud)
在这里,我们返回一个指向零终止序列的指针,该序列char可以传递给Python c_char_p.你不能只是CString因为它是Rust结构而不能直接在C代码中使用而返回- 它包装Vec<u8>并且实际上由三个指针大小的整数组成.它与C char*直接不兼容.我们需要从中获取原始指针.CString::into_raw()方法执行此操作 - 它使用CStringby值,"忘记"它,因此它的分配不会被销毁,并返回*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); }
}
Run Code Online (Sandbox Code Playgroud)
CString::from_raw()方法接受一个*mut c_char指针并从中创建一个CString实例,计算进程中底层以零结尾的字符串的长度.此操作意味着所有权转移,因此结果CString值将拥有分配,并且当它被删除时,分配将被释放.这正是我们想要的.