Rust 调用 C++ 库lib dll:手把手教你做 “语言转接头”

Rust 调用 C++ 库lib dll:手把手教你做 “语言转接头”

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.soLinux规矩:库名前加lib,后缀.sog++ -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 .


完美运行!就像用特制钥匙打开了带密码的箱子,用完还乖乖锁好 —— 这波操作连编译器都得夸你靠谱。

避坑指南:这些错误能让你哭

  1. 忘了写extern “C”:C++ 函数名被加密,Rust 找不到,报错 “未定义引用”—— 就像转接头没插好,充不上电。
  2. 不调用destroy函数:C++ 对象没人销毁,内存泄漏 —— 就像借了箱子不还,下次想用发现都被你弄丢了。
  3. 用null指针调用函数:程序直接崩溃 —— 就像对着空气拧钥匙,手劲大了能把钥匙拧断。
  4. 返回局部变量的引用:C++ 函数里创建的局部变量,返回引用给 Rust,结果变量被销毁,变成悬垂引用 —— 就像借你个冰淇淋,你还没吃我就把它扔了。

总结:转接头的艺术

Rust 调用 C++ 库的核心就是:用 C 语言做转接头,让两个 “接口不同” 的语言能顺畅沟通。记住三个词:


  • extern “C”:给 C++ 函数贴 “明文标签”
  • 共享库:把 C++ 代码打包成 “可借用的工具箱”
  • unsafe块:告知 Rust“这段我盯着,出问题算我的”


只要转接头做对了,祖传工具箱(C++ 库)就能在新项目(Rust)里继续发光发热 —— 毕竟好东西,可不能由于接口不一样就浪费了~

标题

  1. 《Rust 调用 C++ 库:手把手教你做 “语言转接头”》
  2. 《从函数到类:Rust 与 C++ 的 “跨语言合作” 指南》

简介

本文用 “转接头” 类比通俗解释 Rust 调用 C++ 库的原理,通过两个完整案例(基础函数调用和类操作),详细讲解从 C++ 代码编写、编译成共享库到 Rust 调用的全流程,附带避坑指南,让你轻松实现跨语言协作。

关键词

#Rust 调用 C++ #跨语言交互 #FFI #共享库 #extern “C”

© 版权声明

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
none
暂无评论...