在你的 C 代码中使用一点 Rust
在 C 或 C++ 项目中使用 Rust 代码主要包括两个部分。
- 在 Rust 中创建 C 友好的 API
- 将你的 Rust 项目嵌入到外部构建系统中
除了 cargo
和 meson
,大多数构建系统没有原生的 Rust 支持。因此,你最好只使用 cargo
来编译你的 crate 和任何依赖项。
设置项目
像往常一样创建一个新的 cargo
项目。
有一些标志可以告诉 cargo
发出系统库,而不是其常规的 rust 目标。如果你希望你的库名称与 crate 的其余部分不同,这也允许你为你的库设置不同的输出名称。
[lib]
name = "your_crate"
crate-type = ["cdylib"] # Creates dynamic lib
# crate-type = ["staticlib"] # Creates static lib
构建 C
API
由于 C++ 没有稳定的 ABI 供 Rust 编译器定位,我们在不同语言之间的任何互操作性都使用 C
。在 C 和 C++ 代码中使用 Rust 时也不例外。
#[no_mangle]
Rust 编译器对符号名称的修饰方式与原生代码链接器期望的不同。因此,任何 Rust 导出以供 Rust 外部使用的函数都需要被告知不要被编译器修饰。
extern "C"
默认情况下,你在 Rust 中编写的任何函数都将使用 Rust ABI(它也没有稳定)。相反,在构建面向外部的 FFI API 时,我们需要告诉编译器使用系统 ABI。
根据你的平台,你可能希望定位特定的 ABI 版本,这些版本记录在这里。
将这些部分放在一起,你会得到一个大致如下的函数。
#[no_mangle]
pub extern "C" fn rust_function() {
}
就像在你的 Rust 项目中使用 C
代码一样,你现在需要将数据转换为应用程序的其余部分可以理解的形式。
链接和更大的项目上下文。
那么,这解决了问题的一半。现在如何使用它呢?
这很大程度上取决于你的项目和/或构建系统
cargo
将创建一个 my_lib.so
/my_lib.dll
或 my_lib.a
文件,具体取决于你的平台和设置。这个库可以简单地被你的构建系统链接。
但是,从 C 调用 Rust 函数需要一个头文件来声明函数签名。
你的 Rust-ffi API 中的每个函数都需要有一个相应的头文件函数。
#[no_mangle]
pub extern "C" fn rust_function() {}
那么会变成
void rust_function();
等等。
有一个工具可以自动化这个过程,叫做 cbindgen,它分析你的 Rust 代码,然后从中为你的 C 和 C++ 项目生成头文件。
此时,从 C 使用 Rust 函数就像包含头文件并调用它们一样简单!
#include "my-rust-project.h"
rust_function();