
Rust 调用 C++ 库:就像用苹果充电器给安卓手机充电
想象一下:你有个超好用的祖传工具箱(C++ 库),但你新买的螺丝刀(Rust 项目)接口不一样,插不进去。这时候你需要个转接头(C 语言接口)—— 这就是 Rust 调用 C++ 库的核心逻辑。今天咱们就手把手教你做这个 “转接头”,让新工具用上老宝贝!
为啥不能直接 “插”?C++ 的 “加密文件名” 坑
C++ 编译器有个怪癖:会给函数名 “加密”(专业叫 “名称修饰”)。列如你写个add(1,2),编译器可能偷偷改成_Z3addii。但 Rust 是个老实人,它只认识 “明文” 函数名,看到加密后的直接懵圈:“这啥玩意儿?”
解决办法特简单:让 C++ 用 C 语言的 “明文规则” 导出函数 —— 就像给工具箱贴个 “通用接口” 标签,Rust 一看就懂。
案例 1:调用 C++ 的普通函数(借个计算器)
咱们先从简单的来:让 C++ 实现一个加法函数,Rust 来调用。
步骤 1:写 C++ 代码和 C 接口(做转接头)
新建math.cpp(C++ 实现)和math.h(C 接口):
cpp
运行
// math.h
// 告知C++编译器:下面这段按C的规矩来(不加密函数名)
#ifdef __cplusplus
extern "C" {
#endif
// C风格接口:Rust看得懂的“明文”函数
int add(int a, int b);
float multiply(float x, float y);
#ifdef __cplusplus
}
#endif
cpp
运行
// math.cpp
#include "math.h"
// C++实现(内里随意浪,对外保持低调)
int add(int a, int b) {
return a + b; // 简单到不像C++
}
float multiply(float x, float y) {
return x * y; // 偷偷用C++的语法也没人管
}
步骤 2:把 C++ 编译成 “可共享的工具箱”(共享库)
在 Linux/macOS 上用 g++ 编译:
bash
# 生成位置无关代码(PIC),适合做共享库
g++ -c -fPIC math.cpp -o math.o
# 打包成libmath.so(Linux规矩:库名前加lib,后缀.so)
g++ -shared math.o -o libmath.so
Windows 上用 MSVC 编译成.dll(换汤不换药):
cmd
cl /c /EHsc math.cpp
link /DLL math.obj /OUT:math.dll
步骤 3:写 Rust 代码 “用工具箱”(调用函数)
新建 Rust 项目:
bash
cargo new rust_call_cpp && cd rust_call_cpp
修改src/main.rs:
rust
// 声明要调用的C函数(告知Rust:有这两个函数,参数返回值啥样)
extern "C" {
fn add(a: i32, b: i32) -> i32;
fn multiply(x: f32, y: f32) -> f32;
}
fn main() {
let a = 10;
let b = 20;
// 调用C++函数必须放unsafe块里(Rust:“这部分我管不了,你自己负责安全哈”)
let sum = unsafe { add(a, b) };
let product = unsafe { multiply(3.5, 4.0) };
println!("{} + {} = {}", a, b, sum); // 10 + 20 = 30
println!("3.5 × 4.0 = {}", product); // 3.5 × 4.0 = 14
}
步骤 4:让 Rust 找到 “工具箱”(编译运行)
告知 Rust 库在哪,然后运行:
bash
# -L . 表明在当前目录找库
cargo run -L .
成功啦!就像用转接头插上了旧工具箱,新螺丝刀终于能用了~
案例 2:调用 C++ 的类(借个带密码的箱子)
C++ 的类有构造函数、成员函数,就像带密码锁的箱子。咱们得给它做个 “钥匙接口”,让 Rust 能开、能用、能锁。
步骤 1:写 C++ 类和 C 包装接口
新建counter.h、counter.cpp:
cpp
运行
// counter.h
#ifdef __cplusplus
extern "C" {
#endif
// 用结构体指针伪装C++类(Rust只需要知道“这是个箱子”,不用知道里面啥样)
typedef struct Counter Counter;
// C风格接口:创建(开锁)、使用(操作)、销毁(锁箱)
Counter* counter_create(int initial); // 构造函数包装
void counter_add(Counter* c, int num); // 成员函数包装
int counter_get(Counter* c); // 成员函数包装
void counter_destroy(Counter* c); // 析构函数包装(必须有!不然箱子丢了)
#ifdef __cplusplus
}
// C++类的真正实现(藏在接口后面)
class Counter {
private:
int value;
public:
Counter(int initial) : value(initial) {} // 构造:初始值
void add(int num) { value += num; } // 加数字
int get() const { return value; } // 取当前值
};
#endif
cpp
运行
// counter.cpp
#include "counter.h"
// 实现C接口(钥匙的具体用法)
extern "C" {
Counter* counter_create(int initial) {
return new Counter(initial); // 开锁:新建对象
}
void counter_add(Counter* c, int num) {
if (c) c->add(num); // 操作:调用成员函数(先检查箱子没坏)
}
int counter_get(Counter* c) {
return c ? c->get() : -1; // 取值:安全检查
}
void counter_destroy(Counter* c) {
delete c; // 锁箱:销毁对象(必须还!不然丢了算你的)
}
}
步骤 2:编译成共享库
bash
g++ -c -fPIC counter.cpp -o counter.o
g++ -shared counter.o -o libcounter.so
步骤 3:Rust 调用 C++ 类(用带密码的箱子)
修改src/main.rs:
rust
extern "C" {
// 声明C接口函数(钥匙)
fn counter_create(initial: i32) -> *mut Counter;
fn counter_add(c: *mut Counter, num: i32);
fn counter_get(c: *mut Counter) -> i32;
fn counter_destroy(c: *mut Counter);
}
// 空结构体:给指针贴标签(告知Rust“这是Counter类型的箱子”)
struct Counter;
fn main() {
// 创建计数器(开锁)
let counter = unsafe { counter_create(100) };
if counter.is_null() {
eprintln!("箱子打不开!");
return;
}
// 操作计数器(用箱子)
unsafe {
counter_add(counter, 50); // 加50
counter_add(counter, 30); // 再加30
println!("当前值:{}", counter_get(counter)); // 180
counter_destroy(counter); // 锁箱子(必须做!不然内存泄漏)
}
}
步骤 4:编译运行
bash
cargo run -L .
完美运行!就像用特制钥匙打开了带密码的箱子,用完还乖乖锁好 —— 这波操作连编译器都得夸你靠谱。
避坑指南:这些错误能让你哭
- 忘了写extern “C”:C++ 函数名被加密,Rust 找不到,报错 “未定义引用”—— 就像转接头没插好,充不上电。
- 不调用destroy函数:C++ 对象没人销毁,内存泄漏 —— 就像借了箱子不还,下次想用发现都被你弄丢了。
- 用null指针调用函数:程序直接崩溃 —— 就像对着空气拧钥匙,手劲大了能把钥匙拧断。
- 返回局部变量的引用:C++ 函数里创建的局部变量,返回引用给 Rust,结果变量被销毁,变成悬垂引用 —— 就像借你个冰淇淋,你还没吃我就把它扔了。
总结:转接头的艺术
Rust 调用 C++ 库的核心就是:用 C 语言做转接头,让两个 “接口不同” 的语言能顺畅沟通。记住三个词:
- extern “C”:给 C++ 函数贴 “明文标签”
- 共享库:把 C++ 代码打包成 “可借用的工具箱”
- unsafe块:告知 Rust“这段我盯着,出问题算我的”
只要转接头做对了,祖传工具箱(C++ 库)就能在新项目(Rust)里继续发光发热 —— 毕竟好东西,可不能由于接口不一样就浪费了~
标题
- 《Rust 调用 C++ 库:手把手教你做 “语言转接头”》
- 《从函数到类:Rust 与 C++ 的 “跨语言合作” 指南》
简介
本文用 “转接头” 类比通俗解释 Rust 调用 C++ 库的原理,通过两个完整案例(基础函数调用和类操作),详细讲解从 C++ 代码编写、编译成共享库到 Rust 调用的全流程,附带避坑指南,让你轻松实现跨语言协作。
关键词
#Rust 调用 C++ #跨语言交互 #FFI #共享库 #extern “C”


