你的C配点Rust

在C或者C++中使用Rust代码通常由两部分组成。

  • 用Rust创造一个C友好的API
  • 将你的Rust项目嵌入一个外部的编译系统

除了cargomeson,大多数编译系统没有原生Rust支持。因此你最好只用cargo编译你的crate和依赖。

设置一个项目

像往常一样创建一个新的cargo项目。有一些标志可以告诉cargo去生成一个系统库,而不是常规的rust目标文件。如果你想要它与你的crate的其它部分不一样,这也允许你为你的库设置一个不同的输出名。

  1. [lib]
  2. name = "your_crate"
  3. crate-type = ["cdylib"] # 生成动态链接库
  4. # crate-type = ["staticlib"] # 生成静态链接库

构建一个C API

因为对于Rust编译器来说,C++没有稳定的ABI,因此我们使用C表示不同语言间的互用性。在C和C++代码的内部使用Rust时也不例外。

#[no_mangle]

Rust对符号名的修饰与本机的代码链接器所期望的不同。因此,需要告知任何被Rust导出到Rust外部去使用的函数不要被编译器修饰。

extern “C”

默认,任何用Rust写的函数将使用Rust ABI(这也不稳定)。相反,当编译面向外部的FFI APIs时,我们需要告诉编译器去使用系统ABI 。

取决于你的平台,你可能需要针对一个特定的ABI版本,其记录在这里


把这些部分放在一起,你得到一个函数,其粗略看起来像是这个。

  1. #[no_mangle]
  2. pub extern "C" fn rust_function() {
  3. }

就像在你的Rust项目中使用C代码时那样,你现在将需要把数据转换为应用其它部分可以理解的形式。

链接和更大的项目上下文

问题只解决了一半。

你现在要如何使用它?

这很大程度上取决于你的项目或者编译系统

cargo将生成一个my_lib.so/my_lib.dll或者my_lib.a文件,取决于你的平台和配置。可以通过编译系统简单地链接这个库。

然而,从C调用一个Rust函数要求一个头文件去声明函数的签名。

在你的Rust-ffi API中的每个函数需要有一个相关的头文件函数。

  1. #[no_mangle]
  2. pub extern "C" fn rust_function() {}

将会变成

  1. void rust_function();

等等。

这里有个工具可以自动化这个过程,被叫做cbindgen,其会分析你的Rust代码然后从它为你的C和C++项目生成头文件。

此时从C中使用Rust函数非常简单,只需包含头文件和调用它们!

  1. #include "my-rust-project.h"
  2. rust_function();