Rust函数导出供C++调用必须同时满足:extern "C"指定C ABI、#[no_mangle]禁用符号修饰、参数返回值为C兼容类型;cxx::bridge定义跨语言契约,未声明的类型/函数不可见;需确保Rust运行时初始化,内存生命周期须双方严格对齐。

rust函数导出时必须用 extern "C" 且禁用 name mangling
直接从 C++ 调用 Rust 函数,Rust 端必须显式声明为 C ABI 接口。否则 C++ 链接器找不到符号,或因 name mangling 导致 undefined reference 错误。
常见错误:只写 pub fn 不加 extern "C",或者漏掉 #[no_mangle] —— Rust 默认会把 add 编译成类似 _ZN3lib3add17habc123... 的符号名,C++ 完全无法识别。
-
extern "C"告诉 Rust 用 C 调用约定(参数压栈顺序、栈清理责任等) -
#[no_mangle]禁用符号重命名,确保导出的函数名就是源码里写的那个 - 返回值和参数类型只能是 C 兼容类型:
i32、f64、*const u8、*mut i32等;不能直接传String、Vec、&str
示例(Rust 端):
#[no_mangle]
pub extern "C" fn compute_sum(a: i32, b: i32) -> i32 {
a + b
}
cxx-rs 绑定中 #[cxx::bridge] 的作用不是“自动互通”,而是定义契约边界
cxx-rs 不是魔法桥,它生成的是严格类型检查的胶水代码,所有跨语言交互必须在 #[cxx::bridge] 模块里提前声明。没在这里出现的类型、函数、结构体,C++ 根本看不到。
立即学习“C++免费学习笔记(深入)”;
容易踩的坑是以为写了 Rust 函数就能被 C++ 调用,结果编译时报 use of undeclared identifier 或链接失败。
-
#[cxx::bridge]内部要分extern "Rust"和extern "C++"两块,分别描述各自语言侧的接口 - Rust 结构体若需被 C++ 使用,必须加
#[derive(Debug, Clone)]且字段全为 C 兼容类型;含Box、Rc、闭包的结构体不能跨语言传递 - C++ 类型如
std::string、std::vector可以用,但需在#[cxx::bridge]中显式声明为string、vector<i32>等,并依赖 cxx 自带绑定
示例片段(Rust):
#[cxx::bridge]
mod ffi {
extern "Rust" {
fn process_data(input: &str) -> Box<str>;
}
extern "C++" {
include!("my_header.h");
type MyCppClass;
fn create_instance() -> UniquePtr<MyCppClass>;
}
}
C++ 侧调用前必须确保 Rust 运行时已初始化,尤其涉及 std 或分配器时
如果 Rust 代码用了 String::from、Vec::new 或任何依赖全局分配器的操作,而 C++ 主程序没触发过 Rust 运行时初始化,首次调用可能 crash 在 malloc 或空指针解引用上,错误信息常是 segmentation fault 或 abort()。
这不是 cxx-rs 的 bug,而是 Rust std 的惰性初始化机制导致的 —— 它依赖第一个 std:: 符号被加载时才 setup allocator 和 panic handler。
- 最稳妥做法:在 C++
main()开头调用一次空的 Rust 函数(比如rust_init_early()),该函数内部只做std::hint::black_box(0)或访问一个static变量即可触发初始化 - 避免在 DSO(动态库)中首次调用分配操作;若必须,可手动调用
std::alloc::set_global_allocator(极少见,通常不推荐) - 纯
no_std场景无此问题,但也就意味着不能用String、Vec、println!等
字符串和切片传递必须手动管理生命周期,Rust 不会替你 hold C++ 内存
C++ 传给 Rust 的 const char* 或 std::string,Rust 函数返回的 UniquePtr<CxxString> 或 Pin<Box<[i32]>>,背后全是裸指针。谁分配、谁释放,必须双方对齐,否则 double-free 或 use-after-free。
典型错误:C++ 把栈上字符串地址传过去,Rust 试图长期保存指针;或 Rust 返回了堆内存,C++ 忘记调用 .into_raw() 对应的释放逻辑。
- 传入字符串建议统一用
SharedPtr<CxxString>或&CxxString(由 cxx 自动管理 refcount) - 传出字符串优先用
UniquePtr<CxxString>,C++ 侧用完后自动析构(前提是没调用.release()) - 切片(
Span<const i32>)是零拷贝视图,但 lifetime 必须短于原始数据;不要把它存进类成员或跨异步回调使用
示例(C++):
auto result = rust::process_data("hello");
// result 是 UniquePtr<CxxString>,离开作用域自动释放
Rust 和 C++ 之间的安全边界不在语法层面,而在每个指针、每块内存、每次跨语言调用的 ownership 和 lifetime 约定上。cxx-rs 帮你挡住了大部分类型错误,但没挡住你绕过它的尝试。



















