这时就尴尬了 , 此时的字符串是 Rust 里面创建的,转成原始指针之后,Rust 将不再管理相应的堆内存(因为 into_raw 将所有权转移走了),此时就需要手动堆内存了 。
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()print(girl.name.decode("utf-8")) # S 老师print(girl.age) # 18# 直接传递 girl 即可,会释放 girl 里面的字段在堆区的内存py_lib.free(girl)此时就不会出现内存泄露了,在 free 的时候,将变量 girl 传进去,释放掉内部字段占用的堆内存 。
当然,Rust 也可以返回结构体指针,通过 Box<T> 实现 。
#[no_mangle]pub extern "C" fn create_struct() -> *mut Girl { let name = CString::new("S 老师").unwrap().into_raw(); let age = 18; Box::into_raw(Box::new(Girl { name, age }))}注意:之前是 name 字段在堆上 , 但结构体实例在栈上 , 现在 name 字段和结构体实例都在堆上 。
然后 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), ]# 此时返回值类型就变成了 c_void_p# 当返回指针时 , 建议将返回值设置为 c_void_ppy_lib.create_struct.restype = c_void_p# 拿到指针(一串整数)ptr = py_lib.create_struct()# 将指针转成指定的类型,而类型显然是 POINTER(Girl)# 调用 POINTER(T) 的 contents 方法,拿到相应的结构体实例girl = cast(ptr, POINTER(Girl)).contents# 访问具体内容print(girl.name.decode("utf-8")) # S 老师print(girl.age) # 18# 释放堆内存,这里的释放分为两步,并且顺序不能错# 先 free(girl),释放掉内部字段(name)占用的堆内存# 然后 free(c_void_p(ptr)),释放掉结构体实例 girl 占用的堆内存py_lib.free(girl)py_lib.free(c_void_p(ptr))不难理解,只是在释放结构体实例的时候需要多留意,如果内部有字段占用堆内存 , 那么需要先将这些字段释放掉 。而释放的方式是将结构体实例作为参数传给 free 函数,然后再传入 c_void_p 释放结构体实例 。
回调函数最后看一下 Python 如何传递函数给 Rust,因为 Python 和 Rust 之间使用的是 C ABI,所以函数必须遵循 C 的标准 。
// calc 接收三个参数,前两个参数是 *const i32// 最后一个参数是函数,它接收两个 *const i32,返回一个 i32#[no_mangle]pub extern "C" fn calc( a: *const i32, b: *const i32, op: extern "C" fn(*const i32, *const i32) -> i32) -> i32{ op(a, b)}然后看看 Python 如何传递回调函数 。
from ctypes import *py_lib = CDLL("../py_lib/target/debug/libpy_lib.dylib")# 基于 Python 函数创建 C 函数,通过 @CFUNCTYPE() 进行装饰# CFUNCTYPE 第一个参数是返回值类型,剩余的参数是参数类型@CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))def add(a, b): # a、b 为 int *,通过 .contents.value 拿到具体的值 return a.contents.value + b.contents.value@CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))def sub(a, b): return a.contents.value - b.contents.value@CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))def mul(a, b): return a.contents.value * b.contents.value@CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))def div(a, b): return a.contents.value // b.contents.valuea = pointer(c_int(10))b = pointer(c_int(2))print(py_lib.calc(a, b, add)) # 12print(py_lib.calc(a, b, sub)) # 8print(py_lib.calc(a, b, mul)) # 20print(py_lib.calc(a, b, div)) # 5
推荐阅读
- 你知道 Python 其实自带了小型数据库吗
- 解密Java连接MySQL的最佳实践:选择适合你的方式
- QQ如何用指纹解锁
- 怎么成为淘宝买菜的团长 如何申请成为淘宝买菜团长
- 如何制作三角形笔刷ps,ps该如何才可以画出三角形
- 在拼多多上如何修改评价,拼多多怎么修改评价等级
- cdr中应该如何画波浪线
- cdr2020如何合并打印,cdr该如何才可以进行打印
- 水泥路面起砂如何处理 水泥路面起砂如何处理视频
- 装修公司如何选 装修公司如何选好门店
