不能直接用 extern "C" 调用 Rust,因其仅解决函数名和调用约定,不处理跨语言类型传递、panic 穿透、所有权边界等问题;cxx 通过自动生成双向绑定胶水代码并强制显式类型标注来解决这些缺陷。
很多人试过在 C++ 里写 extern "C" void rust_func();,再链接 libmyrust.a,结果遇到符号未定义、ABI 不兼容、panic 崩溃或内存泄漏。根本原因是:Rust 的 #[no_mangle] + extern "C" 只解决函数名导出和调用约定,不处理:字符串/Vec/Result/Option 等类型跨语言传递、Rust panic 穿透到 C++、所有权边界模糊导致 double-free。cxx 就是为堵住这些洞而生的。
核心不是手写头文件,而是声明一个 extern "C++" 模块,让 cxx 自动生成 C++ 头文件和 Rust FFI stub。关键点:
cxx::UniquePtr、cxx::Str、cxx::Vector;Rust 侧对应 UniquePtr、&CxxString、Vec
cxx::UniquePtr 或传入可变引用参数std::panic::catch_unwind 拦截,否则直接 abort
// src/lib.rs
#[cxx::bridge]
mod ffi {
unsafe extern "C++" {
include!("include/my_api.h");
type MyData;
fn process_data(data: &MyData) -> i32;
}
#[namespace = "rustlib"]
extern "Rust" {
fn rust_process(data: &[u8]) -youjiankuohaophpcn ResultzuojiankuohaophpcnVeczuojiankuohaophpcnu8youjiankuohaophpcn, Stringyoujiankuohaophpcn;
}}
fn rust_process(data: &[u8]) -> Result, String> {
std::panic::c
atch_unwind(|| {
// 实际逻辑
Ok(data.to_vec())
}).unwrap_or_else(|| Err("panic in rust_process".to_owned()))
}
CMake 中正确集成 cxx 构建流程
不能把 Rust crate 当普通静态库链接。cxx 要求 Rust 编译产物是 cdylib(带完整符号表的动态库),且 C++ 编译器必须能读取 cxx 生成的头文件。常见错误:
Cargo.toml 里设 crate-type = ["cdylib"]
find_package(cxx) 加载 cxx 提供的 CXXTargets.cmake
add_subdirectory 引入 cxx 后,没用 cxx_add_crate 而是手动 add_library
# CMakeLists.txt
find_package(cxx REQUIRED)
cxx_add_crate(
NAME myrustlib
TYPE cdylib
CARGO_TARGET_DIR ${CMAKE_BINARY_DIR}/rust
)
target_include_directories(myrustlib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE myrustlib)
cxx 默认不抛 C++ 异常,panic 会终止进程。若要转成 std::runtime_error,得在 Rust 层手动封装:
catch_unwind,失败时返回 Result
if (res.err()) throw std::runtime_error(res.err().to_string());
cxx::UniquePtr 的析构由 C++ 控制,Rust 不参与;但 cxx::String 和 cxx::Vector 在 C++ 侧析构时会自动调用 Rust 的 drop 实现最易忽略的是:Rust 返回的 cxx::String 如果被 C++ 多次 move,第二次 move 会触发空指针解引用 —— 因为底层 Box 已被移交。务必保证单次所有权转移。