CXX库提供了一种安全的机制,用于从 Rust 调用 C++ 代码以及从 C++ 调用 Rust 代码。它在 Rust 和 C++ 语义非常相似的领域划定了共同点,并指导程序员在这一领域内有效地表达他们的语言边界。CXX 填补了底层的细节,使得你可以获得一个安全的绑定,避免了在不安全的 C 风格签名上进行外部函数接口(FFI)的陷阱。
通过对类型和函数签名的静态分析,CXX 保护了 Rust 和 C++ 的不变量。然后,它使用一对代码生成器来高效地实现边界,并在构建过程中加入必要的静态断言,以验证正确性。
生成的 FFI 桥接操作在零开销或可忽略的开销下运行,即无需复制、序列化、内存分配或运行时检查。
FFI 签名能够使用来自任意一方的原生数据结构。此外,CXX 为关键的标准库类型(如字符串、向量、Box、unique_ptr 等)提供了内置绑定,以便将这些类型的惯用 API 暴露给另一种语言。
示例
在这个示例中,我们编写了一个 Rust 应用程序,该应用程序调用了一个大型文件 blobstore 服务的 C++ 客户端。Blobstore 支持对不连续缓冲区上传的 put 操作。例如,我们可能正在上传一个环形缓冲区的快照,该快照通常由两部分组成,或者由于其他原因(如 rope 数据结构)分布在内存中的文件片段。
#[cxx::bridge]
mod ffi {
extern "Rust" {
type MultiBuf;
fn next_chunk(buf: &mut MultiBuf) -> &[u8];
}
unsafe extern "C++" {
include!("example/include/blobstore.h");
type BlobstoreClient;
fn new_blobstore_client() -> UniquePtr<BlobstoreClient>;
fn put(&self, buf: &mut MultiBuf) -> Result<u64>;
}
}
现在我们只需提供 extern “Rust” 块中所有内容的 Rust 定义和 extern “C++” 块中所有内容的 C++ 定义,就可以安全地进行双向调用。
后面章节将详细介绍这个 blobstore 示例的完整版本,包括所有的 Rust 代码和 C++ 代码。代码也可以在 https://github.com/dtolnay/cxx 的 demo 目录中以可运行的形式提供。要尝试运行,请在该目录中运行 cargo run。
- demo/src/main.rs
- demo/include/blobstore.h
- demo/src/blobstore.cc
CXX 库的关键优势在于,main.rs 中的 Rust 代码是 100% 普通的、安全的 Rust 代码,使用 Rust 类型的惯用方式工作,而 blobstore.cc 中的 C++ 代码是 100% 普通的 C++ 代码,使用 C++ 类型的惯用方式工作。Rust 代码感觉像 Rust,C++ 代码感觉像 C++,而不是像 C 风格的“FFI 胶水”。