我们知道 Rust 专门提供了 4 个字节 char 类型来表示 unicode 字符 , 但对于外部导出函数来说,使用 char 是不安全的,所以直接使用 u8 和 u32 就行 。
编译之后 , Python 调用:
from ctypes import *py_lib = CDLL("../py_lib/target/debug/libpy_lib.dylib")# u8 除了可以使用 c_byte 包装之外,还可以使用 c_char# 并且 c_byte 里面只能接收整数,而 c_char 除了整数,还可以接收长度为 1 的字节串print(c_byte(97))print(c_char(97))print(c_char(b"a"))"""c_byte(97)c_char(b'a')c_char(b'a')"""# 以上三者是等价的,因为 char 说白了就是个 u8# 指定返回值为 c_byte,会返回一个整数py_lib.get_char.restype = c_byte# c_byte(97)、c_char(97)、c_char(b"a") 都是等价的# 因为它们本质上都是 u8 , 至于 97 也可以解析为 u8print(py_lib.get_char(97)) # 98# 指定返回值为 c_char,会返回一个字符(长度为 1 的 bytes 对象)py_lib.get_char.restype = c_charprint(py_lib.get_char(97)) # b'b'py_lib.get_unicode.restype = c_wcharprint(py_lib.get_unicode(c_wchar("嘿"))) # 嘿# 直接传一个 u32 整数也可以,因为 unicode 字符底层就是个 u32print(py_lib.get_unicode(ord("憨"))) # 批以上就是字符类型的操作,比较简单 。

文章插图
字符串类型再来看看字符串,我们用 Rust 实现一个函数,它接收一个字符串 , 然后返回大写形式 。
use std::ffi::{CStr, CString};use std::os::raw::c_char;#[no_mangle]pub extern "C" fn to_uppercase(s: *const c_char) -> *mut c_char { // 将 *const c_char 转成 &CStr let s = unsafe { CStr::from_ptr(s) }; // 将 &CStr 转成 &str // 然后调用 to_uppercase 转成大写,得到 String let s = s.to_str().unwrap().to_uppercase(); // 将 String 转成 *mut char 返回 CString::new(s).unwrap().into_raw()}解释一下里面的 CStr 和 CString,在 Rust 中,CString 用于创建 C 风格的字符串(以 结尾),拥有自己的内存 。关键的是,CString 拥有值的所有权 , 当实例离开作用域时,它的析构函数会被调用,相关内存会被自动释放 。而 CStr,它和 CString 之间的关系就像 str 和 String 的关系,所以 CStr 一般以引用的形式出现 。并且 CStr 没有 new 方法,不能直接创建,它需要通过 from_ptr 方法从原始指针转化得到 。
然后指针类型是 *const 和 *mut,分别表示指向 C 风格字符串的首字符的不可变指针和可变指针,它们的区别主要在于指向的数据是否可以被修改 。如果不需要修改,那么使用 *const 会更安全一些 。
我们编写 Python 代码测试一下 。
from ctypes import *py_lib = CDLL("../py_lib/target/debug/libpy_lib.dylib")s = "hello 古明地觉".encode("utf-8")# 默认是按照整型解析的,所以不指定返回值类型的话,会得到脏数据print(py_lib.to_uppercase(c_char_p(s)))"""31916096"""# 指定返回值为 c_char_p,表示按照 char * 来解析py_lib.to_uppercase.restype = c_char_pprint( py_lib.to_uppercase(c_char_p(s)).decode("utf-8"))"""HELLO 古明地觉"""从表面上看似乎挺顺利的 , 但背后隐藏着内存泄露的风险,因为 Rust 里面创建的 CString 还驻留在堆区 , 必须要将它释放掉 。所以我们还要写一个函数,用于释放字符串 。use std::ffi::{CStr, CString};use std::os::raw::c_char;#[no_mangle]pub extern "C" fn to_uppercase(s: *const c_char) -> *mut c_char { let s = unsafe { CStr::from_ptr(s) }; let s = s.to_str().unwrap().to_uppercase(); CString::new(s).unwrap().into_raw()}#[no_mangle]pub extern "C" fn free_cstring(s: *mut c_char) { unsafe { if s.is_null() { return } // 基于原始指针创建 CString,拿到堆区字符串的所有权 // 然后离开作用域,自动释放 CString::from_raw(s) };}
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 你知道 Python 其实自带了小型数据库吗
- 解密Java连接MySQL的最佳实践:选择适合你的方式
- QQ如何用指纹解锁
- 怎么成为淘宝买菜的团长 如何申请成为淘宝买菜团长
- 如何制作三角形笔刷ps,ps该如何才可以画出三角形
- 在拼多多上如何修改评价,拼多多怎么修改评价等级
- cdr中应该如何画波浪线
- cdr2020如何合并打印,cdr该如何才可以进行打印
- 水泥路面起砂如何处理 水泥路面起砂如何处理视频
- 装修公司如何选 装修公司如何选好门店
