问题描述
我对 Rust 很陌生.我如何从可在 Python 中使用的 Rust 函数返回 String
?
I'm very new to Rust. How would I return a String
from a Rust function that can be used in Python?
这是我的 Rust 实现:
Here is my Rust implementation:
use std::ffi::CString;
#[no_mangle]
pub extern fn query() -> CString {
let s = CString::new("Hello!").unwrap();
return s;
}
以及调用它的 Python 代码:
And the Python code that calls it:
from ctypes import cdll, c_char_p
lib = cdll.LoadLibrary("target/release/libtest.so")
result = lib.query()
print(c_char_p(result).value)
我在运行时遇到分段错误.
I get a segmentation fault when its run.
使用下面的 Vladimir Matveev 的 Rust 代码,我能够让它与我的 python 代码的更改一起工作:
Using Vladimir Matveev's Rust code below I was able to get it to work with the changes to my python code:
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)
推荐答案
最直接的版本是这样的:
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()
}
这里我们返回一个指向以零结尾的 char
序列的指针,该序列可以传递给 Python 的 c_char_p
.你不能只返回 CString
因为它是 Rust 结构,不应该直接在 C 代码中使用 - 它包装了 Vec
并且实际上由三个指针组成- 大小的整数.它与 C 的 char*
不直接兼容.我们需要从中获取一个原始指针.CString::into_raw()
方法执行此操作 - 它按值消耗 CString
,忘记"它,因此它的分配不会被破坏,并返回一个 *mut c_char
指针到数组的开头.
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.
然而,这样字符串会被泄露,因为我们忘记了它在 Rust 端的分配,而且它永远不会被释放.我不太了解 Python 的 FFI,但解决这个问题最直接的方法是创建两个函数,一个用于生成数据,一个用于释放数据.然后你需要通过调用这个释放函数来释放 Python 端的数据:
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()
方法接受一个 *mut c_char
指针并从中创建一个 CString
实例,计算进程中底层以零结尾的字符串的长度.此操作意味着所有权转移,因此结果 CString
值将拥有分配,当它被删除时,分配被释放.这正是我们想要的.
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.
这篇关于将字符串从 Rust 函数返回到 Python的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!