在你的 C 代码中加入一点 Rust

在 C 或 C++ 项目中使用 Rust 代码主要包括两个部分。

  • 在 Rust 中创建 C 友好的 API
  • 将你的 Rust 项目嵌入到外部构建系统中

除了 cargomeson 之外,大多数构建系统都没有原生 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.dllmy_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();