所有 Rust 示例,都将使用 cargo 和libc 箱子。 每个示例的Cargo.toml
,都包含以下样板:
[dependencies]
libc = "*"
[lib]
crate-type = ["cdylib"]
crate-type = ["cdylib"]
会创建一个动态链接的库。 可查看 Cargo 文档的动态或静态库,了解更多信息.
让我们开始一些更复杂的东西,接受字符串参数。在 Rust 中,字符串由一组u8
切片组成,并保证是有效的 UTF-8,允许NUL
字节,在字符串内部。在 C 中,字符串只是指向一个char
的指针,用一个NUL
字节作为终止 (带整数值0
) 。需要做一些转换工作,才能在处理好这两种表达。
extern crate libc;
use libc::{c_char, uint32_t};
use std::ffi::CStr;
use std::str;
#[no_mangle]
pub extern fn how_many_characters(s: *const c_char) -> uint32_t {
let c_str = unsafe {
assert!(!s.is_null());
CStr::from_ptr(s)
};
let r_str = c_str.to_str().unwrap();
r_str.chars().count() as uint32_t
}
获取一个 Rust 字符串切片 (&str
) 需要几个步骤:
我们必须确保 C 指针不是
NULL
,因为 Rust 引用不能NULL
。(会在 C 语言调用该函数,对应的,在 C 中,字符串只是指向一个char
的指针,所以,该 Rust 函数的字符串参数,会迎来一个 C 指针。)使用
std::ffi::CStr
包装指针。CStr
将根据终止的NUL
,计算字符串的长度。这需要一个unsafe
区块,因为我们将解引用一个原始指针,Rust 编译器无法验证该指针,满足所有安全保证,因此程序员必须这样做。(unsafe
的作用,不是说有多么特殊,只是为了让人们更快,且更好地注意到,存在安全隐患的代码。)确保 C 字符串是有效的 UTF-8 ,并将其转换为 Rust 字符串切片。
使用字符串切片。
Python
#!/usr/bin/env python3
# coding: utf-8
import sys, ctypes
from ctypes import c_uint32, c_char_p
prefix = {'win32': ''}.get(sys.platform, 'lib')
extension = {'darwin': '.dylib', 'win32': '.dll'}.get(sys.platform, '.so')
lib = ctypes.cdll.LoadLibrary(prefix + "string_arguments" + extension)
lib.how_many_characters.argtypes = (c_char_p,)
lib.how_many_characters.restype = c_uint32
print(lib.how_many_characters("göes to élevên".encode('utf-8')))
Python 字符串,必须编码为 UTF-8,才能通过 FFI 边界.
没有评论:
发表评论