You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

从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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 08:02:44