Appearance
34.2 从 C 调用 Rust 库
假设我们要在 Rust 中实现一个把字符串从小写变大写的函数,然后由 C 语言调用这个函数。实现代码如下:
rust
#[no_mangle]
pub extern "C" fn rust_capitalize(s: *mut c_char)
{
unsafe {
let mut p = s as *mut u8;
while *p != 0 {
let ch = char::from(*p);
if ch.is_ascii() {
let upper = ch.to_ascii_uppercase();
*p = upper as u8;
}
p = p.offset(1);
}
}
}
我们在 Rust 中实现这个函数,考虑到 C 语言调用的时候传递的是char *
类型,所以在 Rust 中我们对应的参数类型是*mut std::os::raw::c_char
。这样两边就对应起来了。
这个函数是要被外部的 C 代码调用的,所以一定要用extern "C"
修饰。用#[no_mangle]
修饰主要是为了保证导出的函数名字和源码中的一致。这个并不是必须的,我们还可以使用#[export_name="my_whatever_name"]
来指定导出名字。在某些时候,我们需要导出的函数名恰好在 Rust 中跟某个关键字发生了冲突,就可以用这种方式来规避。
使用如下编译命令,可以生成一个与 C 的 ABI 兼容的静态库。
rust
rustc --crate-type=staticlib capitalize.rs
下面我们再写一个调用这个函数的 C 程序:
rust
#include <stdlib.h>
#include <stdio.h>
// declare
extern void rust_capitalize(char *);
int main() {
char str[] = "hello world";
rust_capitalize(str);
printf("%s\n", str);
return 0;
}
使用如下命令编译链接:
rust
gcc -o main main.c -L. -l:libcapitalize.a -Wl,--gc-sections -lpthread -ldl
可以正确生成可执行程序。执行代码,可见该程序完成了预期中的功能。