不能直接用 extern "C" 调用 Rust,因其仅解决函数名和调用约定,不处理跨语言类型传递、panic 穿透、所有权边界等问题;cxx 通过自动生成双向绑定胶水代码并强制显式类型标注来解决这些缺陷。

为什么不能直接用 extern "C" 调用 Rust?
很多人试过在 C++ 里写 extern "C" void rust_func();,再链接 libmyrust.a,结果遇到符号未定义、ABI 不兼容、panic 崩溃或内存泄漏。根本原因是:Rust 的 #[no_mangle] + extern "C" 只解决函数名导出和调用约定,不处理:字符串/Vec/Result/Option 等类型跨语言传递、Rust panic 穿透到 C++、所有权边界模糊导致 double-free。cxx 就是为堵住这些洞而生的。
用 cxx::bridge 生成双向绑定胶水代码
核心不是手写头文件,而是声明一个 extern "C++" 模块,让 cxx 自动生成 C++ 头文件和 Rust FFI stub。关键点:
- 所有跨语言类型必须显式标注:C++ 侧用
cxx::UniquePtr、cxx::Str、cxx::Vector;Rust 侧对应UniquePtr、&CxxString、Vec - 函数不能返回裸指针或引用,必须包装成
cxx::UniquePtr或传入可变引用参数 - panic 必须用
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::catch_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"] - CMake 没通过
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)
内存与异常:谁负责释放?panic 怎么转成 C++ 异常?
cxx 默认不抛 C++ 异常,panic 会终止进程。若要转成 std::runtime_error,得在 Rust 层手动封装:
- 所有对外函数入口加
catch_unwind,失败时返回Result - C++ 侧检查返回值,手动 throw:
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 已被移交。务必保证单次所有权转移。











