在Go中调用Rust动态库

2020/07/29 posted in  Go
Tags: 

使用cbindgen生成Rust代码的C绑定

编辑Cargo.toml文件

[lib]
name = "speaker"
path = "src/plugins/speaker.rs"
crate-type = ["cdylib"]

[build-dependencies]
cbindgen = "*"

build.rs中调用cbindgen生成头文件

extern crate cbindgen;

fn main() {
    let crate_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();

    cbindgen::Builder::new()
        .with_language(cbindgen::Language::C)
        .with_crate(crate_dir)
        .generate()
        .expect("Unable to generate C bindings.")
        .write_to_file("src/plugins/speaker.h");
}

因为cbindgen默认会生成C++的头文件, 但是cgo只支持C库而不支持C++库, 所以我们需要用with_language(cbindgen::Language::C)的方式指定生成C的头文件
现在我们用Rust写一个简单的动态库

#[no_mangle]
pub extern "C" fn run() {
    println!("Hello, speaker!");
}

执行cargo build命令, 会生成头文件speaker.h和动态库文件libspeaker.dylib

使用cgo运行动态库

package main

/*
#cgo CFLAGS: -I./include
#cgo LDFLAGS: -L./lib -lspeaker

#include <speaker.h>
*/
import "C"

func main() {
    C.run()
}

首先在代码中添加import "C"表示使用cgo特性
import "C"之前的注释中使用#cgo来设置编译和链接阶段的相关参数
CFLAGS表示用于C编译器的选项, 使用-I指定头文件所在的目录
LDFLAGS告诉链接器从哪里找寻库文件(在unix系统中, ld常作为load或者loader的缩写使用), 使用-L指定库所在的目录, 使用-l指定库名