前言

Call C dynamic library in rust文章里面已经介绍了手动绑定c头文件到rust里面, 这下终于可以在rust里面使用c语言遗产。但是这里有个问题,如果要绑定的函数有十几个,时间都花在这种体力活上, 也许唯一的好处是发型越来越资深,哈哈。 作为vim深度使用者,一直坚信,可以重复的工作一定可以自动化。 这里会介绍一种新的方式,在rust 愉快的使用c语言( 内容组织前面可能和 “Call C dynamic library in rust” 有点重复。为了降低阅读难度,只能如此。 如果了解创建动态库的方式,可以直接跳过一、二部分)

一、初始化rust工程

如果是vim写代码的用户,可以直接使用,如果是ide,自行创建工程。

1
cargo new --bin test_rust_call_c

二、生成一个c动态库

如果了解在c里面生成动态库的流程可不看,这个使用简单的add函数(返回两个入参的和),演示流程,至于更多的类型转化可看官方文档。

1.add.h内容

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#ifndef _ADD_H

#ifdef __cplusplus
extern "C" {
#endif

    int add(int a, int b);

#ifdef __cplusplus
"}"
#endif

#endif

2.add.c内容

1
2
3
4
#include "add.h"
int add(int a, int b) {
    return a + b;
}

3.生成add.so

1
gcc -fPIC -shared add.c -o libadd.so

三、自动生成c头文件

1.安装clang

bindgen 依赖clang编译器, 下面是ubuntu安装方式

1
apt install llvm-dev libclang-dev clang

更多方式可看 https://rust-lang.github.io/rust-bindgen/requirements.html

2.安装bindgen

运行下面的命令安装bindgen命令

1
cargo install bindgen

3.生成c/c++绑定文件

bindgen 默认安装~/.cargo/bin目录,如果找不到命令,把~/.cargo/bin 添加到PATH环境变量中。 bindgen 会更加输入的c/c++头文件生成rust绑定文件,如果不指定输出会直接打印到stdout中 如果想把生成后的binding.rs 放到src/binging.rs里面,可以想下面运行命令

1
bindgen add.h -o src/bindings.rs

4.自动生成代码内容

1
2
3
4
5
6
7
// 自动生成rust代码位于 src/binding.rs
/* automatically generated by rust-bindgen 0.56.0 */

extern "C" {
    pub fn add(a: ::std::os::raw::c_int, b: ::std::os::raw::c_int) -> ::std::os::raw::c_int;
}

四、创建mod和指定编译依赖

1.指定编译依赖

如果熟悉c/c++就指定,编译动态库需要指定-l-L 选项,一个指定动态库名称,一个指定动态库搜索路径。 可以写build.rs build.rs是rust里面的编译脚本

1
2
3
4
5
// build.rs rust的编译脚本
fn main() {
    println!("cargo:rustc-link-search=."); // 等于rustc -L . 在当前目录搜索动态库
    println!("cargo:rustc-link-lib=dylib=add"); // 等于rustc -ladd 搜索libadd.so文件
}

2.创建mod

这里使用lib.rs告知编译器,刚刚生成的binding.rs要当成模块。后面方便在main.rs里面调用。

1
echo 'pub mod binding;' > src/lib.rs

五、运行

现在可以在src/main.rs,一睹效果。使用use test_rust_call_c2::binding可以引用刚刚生成的代码。 至于为什么是test_rust_call_c2。这个是 一、初始化rust工程的命令指定的,如果不喜欢这个名字可以在Cargo.toml文件把 name = "test_rust_call_c" 修改掉

1
2
3
4
5
6
use test_rust_call_c2::binding;
fn main() {
    unsafe {
        println!("{}", binding::add(1, 2));
    }
}

参考资料

https://rust-lang.github.io/rust-bindgen/command-line-usage.html