Rust与C接口交互
Rust 对于与其他编程语言的互操作性有着出色的支持。这意味着:
- 从其他语言调用 Rust 函数。
- 从 Rust 调用用其他语言编写的函数。
调用C接口库
以windows下Dll库为例。
C库准备
先通过VS生成一个Dll库,对应的C头文件:
#ifdef CEXPORTLIB_EXPORTS
#define DLL_API __declspec(dllexport)
#else
#define DLL_API __declspec(dllimport)
#endif
#ifdef __cplusplus
extern "C" {
#endiftypedef struct {double real;double imag;}Complex;Complex DLL_API add(Complex first, Complex second);//Complex DLL_API add(const Complex& first, const Complex& second);void DLL_API add_self(Complex& first, const Complex& second);
#ifdef __cplusplus
}
#endif
Rust侧调用
1、build脚本
在Cargo.toml中增加build项:
[package]
...
build = "build.rs"
在根目录(Cargo.toml同一层)增加build.rs(里面main与main.rs中的main没任何关系)
// build.rs
fn main() {// println!("cargo::rustc-link-lib=dylib=stdc++"); // This line may be unnecessary for some environment.println!("cargo::rustc-link-search=./x64/Debug"); // lib所在目录
}
2、rust中声明C库中的接口与结构
#[repr©]用于向 Rust 编译器指定这个 struct 总是使用和 C 代码相同的规则来组织内部成员的内存布局。
// c_handle.rs
#[repr(C)]
#[derive(Debug)]
pub struct Complex {pub real: f64,pub imag: f64,
}
#[link(name = "c_export_lib", kind = "dylib")]
unsafe extern "C" {// 此种方式会转移所有权,调用后first与second都无法再访问// pub fn add(first: Complex, second: Complex) -> Complex;// 此种方式不会转移所有权pub fn add(first: *const Complex, second: *const Complex) -> Complex;pub fn add_self(first: *mut Complex, second: *const Complex) -> ();
}
3、rust中调用接口
let a = c_handle::Complex { real: 1.0, imag: 2.0 };let b = c_handle::Complex { real: 3.0, imag: 4.0 };// let c = unsafe { c_handle::add(a, b) }; // 此种方式后面不能在访问a、blet c = unsafe { c_handle::add(&a, &b) };println!("{:?} + {:?} = {:?}", a, b, c); let mut d = c_handle::Complex { real: 2.0, imag: 2.0 };unsafe {c_handle::add_self(&mut d, &b);}println!("{:?}", d);
导出C兼容接口
生成C兼容的接口库,用于其他语言调用;先生成库工程:cargo new r_export --lib
1、toml配置
修改cargo.toml增加库说明
[package]
build = "build.rs"[lib]
crate-type = ["cdylib"][dependencies][build-dependencies]
cbindgen = "0.29.0"
2、对外接口
通过pub与extern声明对外兼容接口:
#[repr(C)]
pub struct Point {pub x: i32,pub y: i32,
}
#[unsafe(no_mangle)]
pub extern "C" fn create_point(x: i32, y: i32) -> Point {Point { x, y }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn free_point(point: *mut Point) {unsafe {drop(Box::from_raw(point));}
}
#[unsafe(no_mangle)]
pub extern "C" fn hello_world()
{println!("hello world");
}
3、编写build.rs
在build脚本中设定导出(如头文件等)
// build.rs
use std::env;fn main() {let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();cbindgen::Builder::new().with_crate(crate_dir).generate().expect("Unable to generate bindings").write_to_file("r_export.h");
}
编译成功后,会自动生成C头文件r_export.h,以及dll库。