解密 Python 如何调用 Rust 编译生成的动态链接库( 六 )


如果你真的想返回数组的话 , 那么可以将数组拼接成字符串 , 然后返回 。
use std::ffi::{c_char, CString};#[no_mangle]pub extern "C" fn create_array() -> *mut c_char {    // 筛选出 1 到 50 中,能被 3 整除的数    // 并以逗号为分隔符 , 将这些整数拼接成字符串    let vec = (1..=50)        .filter(|c| *c % 3 == 0)        .map(|c| c.to_string())        .collect::<Vec<String>>()        .join(",");    CString::new(vec).unwrap().into_raw()}编译之后交给 Python 调用 。
from ctypes import *py_lib = CDLL("../py_lib/target/debug/libpy_lib.dylib")# 只要是需要释放的堆内存,都建议按照 c_void_p 来解析py_lib.create_array.restype = c_void_p# 此时拿到的就是指针保存的地址,在 Python 里面就是一串整数ptr = py_lib.create_array()# 由于是字符串首字符的地址,所以转成 char * , 拿到具体内容print(cast(ptr, c_char_p).value.decode("utf-8"))"""3,6,9,12,15,18,21,24,27,30,33,36,39,42,45,48"""# 此时我们就将数组拼接成字符串返回了# 但是堆区的 CString 还在,所以还要释放掉,调用 free 函数即可# 注意:ptr 只是一串整数,或者说它就是 Python 的一个 int 对象# 换句话说 ptr 只是保存了地址值,但它不具备指针的含义# 因此需要再使用 c_void_p 包装一下(转成指针),才能传给 free 函数py_lib.free(c_void_p(ptr))因此虽然不建议返回数组,但将数组转成字符串返回也不失为一个办法,当然除了数组,你还可以将更复杂的结构转成字符串返回 。
 传递结构体结构体应该是 Rust 里面最重要的结构之一了,它要如何和外部交互呢?
use std::ffi::c_char;#[repr(C)]pub struct Girl {    pub name: *mut c_char,    pub age: u8,}#[no_mangle]pub extern "C" fn create_struct(name: *mut c_char, age: u8) -> Girl {    Girl { name, age }}因为结构体实例要返回给外部 , 所以它的字段类型必须是兼容的,不能定义 C 理解不了的类型 。然后还要设置 #[repr(C)] 属性,来保证结构体的内存布局和 C 是兼容的 。
下面通过 cargo build 命令编译成动态库,Python 负责调用 。
from ctypes import *py_lib = CDLL("../py_lib/target/debug/libpy_lib.dylib")class Girl(Structure):    _fields_ = [        ("name", c_char_p),        ("age", c_uint8),    ]# 指定 create_struct 的返回值类型为 Girlpy_lib.create_struct.restype = Girlgirl = py_lib.create_struct(    c_char_p("S 老师".encode("utf-8")),    c_uint8(18))print(girl.name.decode("utf-8"))  # S 老师print(girl.age)  # 18调用成功,并且此时是没有内存泄露的 。
当通过 FFI 将数据从 Rust 传递到 Python 时,如果传递的是指针,那么会涉及内存释放的问题 。但如果传递的是值,那么它会复制一份给 Python,而原始的值(这里是结构体实例)会被自动销毁,所以无需担心 。
然后是结构体内部的字段 , 虽然里面的 name 字段是 *mut c_char,但它的值是由 Python 传过来的,而不是在 Rust 内部创建的 , 因此没有问题 。
但如果将 Rust 代码改一下:
use std::ffi::{c_char, CString};#[repr(C)]pub struct Girl {    pub name: *mut c_char,    pub age: u8,}#[no_mangle]pub extern "C" fn create_struct() -> Girl {    let name = CString::new("S 老师").unwrap().into_raw();    let age = 18;    Girl { name, age }}


推荐阅读