我正在围绕C API创建Rust包装器。此C API中的一个函数设置了一个回调并接受一个void指针,该指针将传递给该回调。它存储了对回调和用户数据的引用,供以后使用,因此我正在使用this answer中的最后一个代码部分。
这是我的代码。 Test::trigger_callback(...)
函数旨在模拟调用回调的C库。
extern crate libc;
use libc::c_void;
use std::mem::transmute;
struct Test {
callback: extern "C" fn(data: i32, user: *mut c_void) -> (),
userdata: *mut c_void,
}
extern "C" fn c_callback(data: i32, user: *mut libc::c_void) {
unsafe {
println!("Line {}. Ptr: {}", line!(), user as u64);
let func: &mut Box<FnMut(i32) -> ()> = transmute(user);
println!("Line {}. Data: {:?}", line!(), data);
(*func)(data);
println!("Line {}", line!());
}
}
impl Test {
fn new<F>(func: F) -> Test
where
F: FnMut(i32) -> (),
F: 'static,
{
let func = Box::into_raw(Box::new(Box::new(func)));
println!("Line: {}, Ptr: {}", line!(), func as u64);
Test {
callback: c_callback,
userdata: func as *mut c_void,
}
}
fn trigger_callback(&self, data: i32) {
(self.callback)(data, self.userdata);
}
}
fn main() {
let test = Test::new(|data: i32| {
println!("Inside callback! Data: {}", data);
});
test.trigger_callback(12345);
}
如链接的答案中所述,Box
将闭包存储在堆上是为了使指向该闭包的指针在任意长时间内有效,然后Box
将其关闭Box
是因为它是一个胖指针,但需要将其转换为常规指针,以便可以将其强制转换为空指针。
运行时,此代码会打印出来:
Line: 29, Ptr: 140589704282120
Line 13. Ptr: 140589704282120
Line 15. Data: 12345
Segmentation fault (core dumped)
尝试在extern "C"
函数内部调用闭包时,它将出现段错误。
为什么?据我了解,将闭包放在Box
中,然后使用Box::into_raw(...)
应该将其存储在堆上并“泄漏”内存,因此指针应在程序中有效在跑。哪一部分错了?
答案 0 :(得分:1)
Box::into_raw(Box::new(Box::new(func)));
这不会产生您认为的类型:
= note: expected type `()`
found type `*mut std::boxed::Box<F>`
您认为它是特征对象:
let func: &mut Box<FnMut(i32) -> ()> = transmute(user);
相反,当您将输入值装箱时,使其成为特征对象。我主张在显式行中添加注释以解释每个步骤:
// Trait object with a stable address
let func = Box::new(func) as Box<FnMut(i32)>;
// Thin pointer
let func = Box::new(func);
// Raw pointer
let func = Box::into_raw(func);
Box<FnMut(i32) -> ()>
()
的返回类型是多余的;使用Box<FnMut(i32)>
let func: &mut Box<FnMut(i32) -> ()> = transmute(user);
请尽量避免 避免使用transmute
。通常有一些较小的工具可以使用:
extern "C" fn c_callback(data: i32, user: *mut libc::c_void) {
let user = user as *mut Box<FnMut(i32)>;
unsafe {
(*user)(data);
}
}
避免完全重述同一类型。引入类型别名:
type CallbackFn = Box<FnMut(i32)>;
let user = user as *mut CallbackFn;
let func = Box::new(func) as CallbackFn;
另请参阅: