Rust meets C++:用 Rust 编写 WASM 模块并在 C++ 项目中使用
Rust meets C++:用 Rust 编写 WASM 模块并在 C++ 项目中使用
想用 Rust 的高性能和安全性,又不想放弃 C++ 项目的现有代码库?没问题!将 Rust 代码编译成 WebAssembly (WASM) 模块,然后在 C++ 项目中调用它,这是一种非常实用的解决方案。本文将手把手教你如何实现。
为什么选择 WASM?
WASM 是一种可移植的、体积小、加载快并且可以在浏览器和非浏览器环境中运行的二进制指令格式。它天然具有跨平台性,非常适合作为不同语言之间互操作的桥梁。
准备工作
安装 Rust 和 Cargo: 确保你已经安装了 Rust 编程语言和 Cargo 包管理器。你可以从 https://www.rust-lang.org/ 下载并安装。
安装 wasm-pack:
wasm-pack是一个用于构建、测试和发布 WASM 包的工具。使用 Cargo 安装它:cargo install wasm-pack安装 Emscripten (可选,但推荐): 虽然不是必须的,但 Emscripten 可以帮助你更方便地在 C++ 中加载和使用 WASM 模块。从 https://emscripten.org/ 下载并安装。
步骤 1:创建 Rust WASM 模块
创建一个新的 Rust 项目:
cargo new --lib rust_wasm cd rust_wasm修改
Cargo.toml文件: 添加wasm-bindgen依赖,并指定 crate 类型为cdylib。cdylib类型会生成一个可以被 C 语言链接器使用的动态链接库。[package] name = "rust_wasm" version = "0.1.0" edition = "2021" [lib] crate-type = ["cdylib"] [dependencies] wasm-bindgen = "0.2"编写 Rust 代码: 在
src/lib.rs文件中编写你的 Rust 代码。例如,创建一个简单的函数,用于将两个数字相加:use wasm_bindgen::prelude::*; #[wasm_bindgen] pub fn add(a: i32, b: i32) -> i32 { a + b }#[wasm_bindgen]属性宏会将 Rust 函数暴露给 JavaScript (以及其他 WASM host 环境)。构建 WASM 模块: 使用
wasm-pack构建项目:wasm-pack build --target web这将在
pkg目录下生成 WASM 模块 (.wasm文件) 和 JavaScript 接口文件 (.js文件)。
步骤 2:在 C++ 项目中使用 WASM 模块
创建一个 C++ 项目: 如果你已经有 C++ 项目,可以跳过此步骤。否则,创建一个新的 C++ 项目。
包含必要的头文件: 如果使用 Emscripten,你需要包含 Emscripten 提供的头文件。例如,创建一个
wasm_wrapper.h文件:#ifndef WASM_WRAPPER_H #define WASM_WRAPPER_H #ifdef __cplusplus extern "C" { #endif // 声明 Rust 函数 (需要根据实际情况修改) int add(int a, int b); #ifdef __cplusplus } #endif #endif注意: 你需要根据 Rust 函数的签名来声明 C 函数。
extern "C"确保 C++ 编译器使用 C 的调用约定,这对于与 WASM 模块交互至关重要。加载 WASM 模块: 使用 Emscripten 或其他 WASM 运行时加载 WASM 模块。以下是使用 Emscripten 的一个示例:
#include <iostream> #include <emscripten.h> #include "wasm_wrapper.h" int main() { // 初始化 Emscripten (如果需要) // emscripten_initialize(); // 调用 Rust 函数 int result = add(5, 3); std::cout << "Result from WASM: " << result << std::endl; // 关闭 Emscripten (如果需要) // emscripten_exit(0); return 0; }注意:
emscripten_initialize()和emscripten_exit()可能不是必需的,具体取决于你的 Emscripten 配置和 WASM 模块的需求。编译和链接 C++ 代码: 使用 C++ 编译器编译你的代码,并将 WASM 模块链接到你的项目中。 使用 Emscripten,你可以这样编译:
emcc main.cpp -o main.js rust_wasm/pkg/rust_wasm.wasm -s EXPORTED_FUNCTIONS="['_add']"main.cpp是你的 C++ 代码文件。main.js是 Emscripten 生成的 JavaScript 文件,它会加载和运行 WASM 模块。rust_wasm/pkg/rust_wasm.wasm是你的 WASM 模块的路径。-s EXPORTED_FUNCTIONS="['_add']"指定要导出的 WASM 函数。 非常重要: Emscripten 会在导出的函数名前面加上下划线 (_)。
运行 C++ 代码: 使用 Node.js 或浏览器运行生成的 JavaScript 文件:
node main.js你应该能看到 Rust 函数的返回值打印到控制台。
遇到的问题和解决方案
- 类型不匹配: 确保 Rust 和 C++ 之间的数据类型匹配。例如,Rust 的
i32对应于 C++ 的int。 - 内存管理: WASM 模块和 C++ 代码可能需要共享内存。你需要仔细管理内存,以避免内存泄漏或崩溃。可以使用
wasm-bindgen提供的JsValue和ArrayBuffer来进行更复杂的数据交互。 - 错误处理: 在 Rust 代码中处理错误,并将错误信息传递给 C++ 代码。可以使用
Result类型来表示可能发生的错误。 - Emscripten 配置: Emscripten 的配置可能会比较复杂。参考 Emscripten 的文档,了解如何正确配置 Emscripten。
高级用法
- 使用
wasm-bindgen处理更复杂的数据结构:wasm-bindgen可以自动生成 Rust 和 JavaScript 之间的绑定代码,从而简化数据结构的传递。 - 使用
wee_alloc优化 WASM 模块的大小:wee_alloc是一个为 WASM 设计的内存分配器,可以显著减小 WASM 模块的大小。 - 使用 Rust 的
#[cfg(target_arch = "wasm32")]属性: 可以使用此属性来编写特定于 WASM 平台的代码。
总结
将 Rust 代码编译成 WASM 模块并在 C++ 项目中使用,是一种强大的技术,可以让你充分利用 Rust 的优势,同时保留现有的 C++ 代码库。虽然配置过程可能比较复杂,但一旦配置完成,你就可以轻松地在 Rust 和 C++ 之间进行互操作。记住,仔细处理数据类型匹配、内存管理和错误处理是成功的关键。
希望本文能帮助你入门!祝你编码愉快!