当 Rust 闭包传入 C ..

  • C 代码调用 Rust 闭包

  • 更新

    1
    23.08.03 初始化

导语

一些日常遇到的 rust 问题解决,考虑弄个集合贴. 暂时单发了.

正文

闭包 是个 实现了 (Fn FnMut FnOnce) 的 trait, 其指针指向的是 trait 所以是 胖指针. 

胖指针 传递 给 rust 直接 Box or dyn 即可. 但 C 语言不认识, 所以没法像传递 函数指针一样,在 C 中调用.

假如传递给 C 除了 rust 回调函数, 还可以传递 arg 这一项, 那还有救!.

  • 要求 arg 定义成了 c_void 类型,

C 语言不认识 胖指针,但 rust 函数认识. 一种解决方式: 

  • 指向 闭包的指针 当作 userdata, 在 rust 编写的 callback 函数中 强转 再调用 闭包.
    • 强转转 大饼卷 一切
  • callback 就成了类似下面的写法
1
2
3
4
5
// https://stackoverflow.com/questions/32270030/how-do-i-convert-a-rust-closure-to-a-c-style-callback
extern "C" fn do_something_handler(x: c_int, arg: *mut c_void) -> c_int {
let closure: &mut Box<dyn FnMut(i32) -> bool> = unsafe { mem::transmute(arg) }; // 强转
closure(x as i32) as c_int
}

这样写 强转 太麻烦, 闭包的类型 比较繁琐 -> 泛型

1
2
3
4
5
6
7
8
// https://adventures.michaelfbryan.com/posts/rust-closures-in-ffi/
unsafe extern "C" fn hook<F>(arg: *mut c_void)
where
F: FnMut(),
{
let closure = &mut *(arg as *mut F); // 强转成 F
closure();
}

这个时候直接给 C 传入 hook 和 闭包的指针, 调用. 报错提示 不知道 F 是啥类型.

hook 和 闭包 没有任何形式关联, rust 编译器没有任何信息推断 -> 那随便关联一下即可.

1
2
3
4
5
6
7
/// 没有实际作用, 仅仅是告诉 编译器 F 到底是啥类型
pub fn get_hook<F>(_closure: &F) -> unsafe extern "C" fn(arg: *mut c_void)
where
F: FnMut(), // closure
{
hook::<F> // 返回的还是 hook函数 自身.
}

包裹的这一层让 编译器做了 强转 的工作,皆大欢喜.😂