如何在C语言中调用Rust函数?Rust调用C DLL场景的回调实现示例
Rust可执行程序调用C DLL并实现C端回调Rust函数的最小实现
C DLL端代码编写
C端需要定义与Rust ABI兼容的回调类型,导出接收函数指针的接口:
头文件(cdll.h)
// 定义跨语言回调类型:无返回值,两个入参分别为事件码、字符串指针 typedef void (*rust_callback_t)(int event_code, const char* msg); // 导出供Rust调用的DLL接口,入参为Rust侧传入的函数指针 __declspec(dllexport) void run_with_callback(rust_callback_t callback);
实现文件(cdll.c)
#include "cdll.h" void run_with_callback(rust_callback_t callback) { // 空指针防护 if (!callback) return; // 模拟任务执行过程,在关键节点触发Rust回调 callback(30, "Task initialized"); // 模拟业务耗时逻辑 for (long i = 0; i < 100000000L; i++); callback(100, "Task completed"); }
编译要求:编译时需保持C ABI无名称修饰,不要使用C++的名称改编规则,保证Rust端能找到对应导出符号。
Rust端功能实现
Rust端需要严格对齐C端的接口签名,通过标准库的FFI模块声明导入函数、定义符合ABI要求的回调函数:
核心Rust代码
use std::ffi::c_char; use std::ffi::CStr; // 1:1对齐C端定义的回调类型 type RustCallback = extern "C" fn(event_code: i32, msg: *const c_char); // 声明从C DLL导入的函数 extern "C" { fn run_with_callback(callback: RustCallback); } // 定义供C端调用的Rust回调 // 必须使用extern "C"标注,保证调用约定与C端一致 extern "C" fn rust_event_handler(event_code: i32, msg: *const c_char) { // 处理C端传来的空指针场景 if msg.is_null() { println!("收到事件 代码:{event_code}, 消息为空"); return; } // 将C字符串安全转换为Rust字符串,此处不释放C端内存,避免所有权冲突 let c_message = unsafe { CStr::from_ptr(msg) }; let message = c_message.to_string_lossy(); println!("收到C端事件通知: 进度 {event_code}%,说明: {message}"); } fn main() { // 将Rust函数指针传入C端函数即可完成注册 unsafe { run_with_callback(rust_event_handler); } }
关键实现注意点
- 所有跨FFI边界的函数,必须显式标注
extern "C",不能使用Rust默认的Rust调用约定,否则会因栈布局不匹配触发未定义行为 - 回调函数需要关联Rust侧上下文时,不能直接捕获环境变量的闭包作为回调,需要通过
*mut c_void类型的透传参数,将上下文指针随函数指针一起传给C端,C端调用回调时将该指针回传,由Rust侧负责类型转换和生命周期管理 - 跨语言传递字符串时需明确内存所有权边界:C端分配的内存不要在Rust端主动释放,Rust端传给C端的字符串需通过
CString管理生命周期,确保C端使用期间内存不被回收 - 函数指针的签名必须完全对齐,参数类型、返回值、调用约定的任何偏差都会导致程序直接崩溃
内容的提问来源于stack exchange,提问作者GirkovArpa
相关产品推荐
相关产品推荐

