I am creating a Rust wrapper around a C API. One function in this C API sets a callback and accepts a void pointer which will be passed to the callback. It stores a reference to the callback and user data for later, so I am using the last code section from this answer.
Here is my code. The Test::trigger_callback(...)
function is meant to emulate the C library calling the callback.
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);
}
As mentioned in the linked answer, Box
ing the closure is meant to store it on the heap so that a pointer to it will be valid for an arbitrarily long time, and then Box
ing that Box
is because it's a fat pointer, but needs to be converted to a regular pointer so that it can be cast to a void pointer.
When run, this code prints out:
Line: 29, Ptr: 140589704282120
Line 13. Ptr: 140589704282120
Line 15. Data: 12345
Segmentation fault (core dumped)
It segfaults when trying to call the closure inside of the extern "C"
function.
Why? As far as I understand it, putting the closure in a Box
and then using Box::into_raw(...)
should store it on the heap and 'leak' the memory, so the pointer should be valid for as long as the program is running. What part of this is wrong?