从C调用带函数参数的Rust函数触发段错误问题排查
解决Rust共享对象与C交互时的段错误问题
你猜的完全没错——&dyn Fn() -> usize和C的函数指针根本不兼容,这就是触发段错误的核心原因。
为什么会崩溃?
Rust里的&dyn Fn(...)是trait对象,在64位系统下它是一个16字节的胖指针:
- 前8字节是指向实现该trait的实例数据的指针
- 后8字节是指向vtable(虚函数表)的指针
而C的函数指针是8字节的瘦指针,仅仅是函数在内存中的地址。当你把C的函数指针传给Rust的trait对象参数时,Rust会错误地把这个8字节的指针当成16字节的胖指针来处理——看你提供的汇编代码,mov 0x18(%rcx),%rax这行试图访问偏移0x18的位置,但你的C函数指针根本没有这么多数据,直接访问非法内存,自然触发段错误。
修复方案
要让Rust和C的回调函数兼容,你需要使用C调用约定的普通函数指针,而不是trait对象。具体修改如下:
修改后的Rust代码
#[no_mangle] // 明确指定extern "C"调用约定,保证和C的函数调用规则一致 pub extern "C" fn show_loading_animation( from: usize, to: usize, // 使用C兼容的函数指针类型 progress_in_percentage_fn: extern "C" fn() -> usize, ) { // 正常调用回调函数 let current_progress = progress_in_percentage_fn(); println!("Current progress: {}%", current_progress); // 你的加载动画逻辑... }
修改后的C代码
// 注意:usize在Rust中是无符号的,64位系统对应unsigned long更准确 typedef unsigned long usize; // 声明Rust导出的函数 extern void show_loading_animation(usize, usize, usize (*prog_fn)()); usize progress_reporter() { return 80u; } int main(void) { show_loading_animation(0, 100, &progress_reporter); return 0; }
额外说明
如果之后你需要支持传递Rust闭包给C(而不仅仅是普通C函数),那需要用Box<dyn Fn() -> usize>将闭包装箱,转换成*mut ()传给C,再在Rust里把指针转换回来调用,但这涉及到内存安全的处理(需要确保闭包不会被提前释放)。不过当前你的场景用普通函数指针就完全能解决问题。
内容的提问来源于stack exchange,提问作者phip1611
相关产品推荐
相关产品推荐

