将Rust函数中的String返回给Python

时间:2015-05-28 15:22:33

标签: python rust

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

这是我的Rust实现:

use std::ffi::CString;

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

调用它的Python代码:

from ctypes import cdll, c_char_p

lib = cdll.LoadLibrary("target/release/libtest.so")
result = lib.query()

print(c_char_p(result).value)

运行时出现分段错误。

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

2 个答案:

答案 0 :(得分: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()
}

这里我们返回一个指向零终止序列char的指针,该序列可以传递给Python的c_char_p。你不能只返回CString因为它是Rust结构,它不应该直接在C代码中使用 - 它包装Vec<u8>并且实际上由三个指针大小的整数组成。它直接与C char*不兼容。我们需要从中获取原始指针。 CString::into_raw()方法执行此操作 - 它消耗CString的值,“忘记”它,以便它的分配不会被销毁,并返回一个指向数组开头的*mut c_char指针。 / p>

然而,这样字符串将被泄露,因为我们忘记了它在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值将拥有该分配,并且当它被删除时,分配将被释放。这正是我们想要的。

答案 1 :(得分:1)

这里的问题是您直接返回CString,这与C中字符串的表示形式不对应(您可以看到here DI的源代码1}})。

您应该使用CString返回指向字符串的指针。但是,您需要确保在函数末尾不释放字符串,因为这会导致悬空指针。

我能想到的唯一解决方案是使用s.as_ptr()让生锈忘记变量而不是释放变量。当然,您需要找到一种方法来释放字符串,以避免内存泄漏(参见弗拉基米尔的回答)。

根据我提到的更改,您的Rust代码应如下所示:

forget