从Rust函数返回字符串到Python

2024-05-13 12:01:15 发布

您现在位置:Python中文网/ 问答频道 /正文

我对生锈很陌生。如何从可以在Python中使用的Rust函数返回String?在

以下是我的Rust实现:

use std::ffi::CString;

#[no_mangle]
pub extern fn query() -> CString {
    let s = CString::new("Hello!").unwrap();
    return s;
}

以及调用它的Python代码:

^{pr2}$

我得到了一个分割错误时,它运行。

编辑:使用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)

Tags: 函数no代码stringuselibresultrust
2条回答

最直接的说法是:

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

这里我们返回一个指向chars的以零结尾的序列的指针,该序列可以传递给Python的c_char_p。你不能仅仅返回CString,因为它是Rust结构,不应该直接在C代码中使用——它包装了Vec<u8>,实际上由三个指针大小的整数组成。它与C的char*不直接兼容。我们需要从中得到一个原始指针。^{}方法就是这样做的——它按值使用CString,“忽略”它,这样它的分配就不会被破坏,并返回一个指向数组开头的*mut c_char指针。在

然而,这样的话,字符串就会被泄露,因为我们忘记了它在锈边的分配,它永远也不会被释放。我不太了解Python的FFI,但是解决这个问题最直接的方法是创建两个函数,一个用于生成数据,另一个用于释放数据。然后需要通过调用以下释放函数从Python端释放数据:

^{pr2}$

^{}方法接受一个*mut c_char指针并从中创建一个CString实例,计算进程中底层以零结尾的字符串的长度。此操作意味着所有权转移,因此结果CString值将拥有分配,当它被删除时,分配将被释放。这正是我们想要的。在

这里的问题是您直接返回一个CString,它与C中的字符串表示不对应(您可以看到here的源代码CString)。在

应该使用s.as_ptr()返回指向字符串的指针。但是,您需要确保在函数末尾没有释放字符串,因为这将导致指针悬空。在

我能想到的唯一解决方案是使用forget让rust忘记变量,而不是释放它。当然,您需要在以后找到释放字符串的方法,以避免内存泄漏(参见Vladimir的答案)。在

根据我提到的更改,您的Rust代码应该是以下内容:

use std::ffi::CString;
use std::mem;

#[no_mangle]
pub extern fn query() -> *const i8 {
    let s = CString::new("Hello!").unwrap();
    let ptr = s.as_ptr();
    mem::forget(s);
    return ptr;
}

相关问题 更多 >