我目前正在研究 FFI 库的安全绑定,这是我遇到的问题的最小实现。
fn test_weird_thing() {
use std::sync::Mutex;
struct WeirdThing<'a> {
vec: Mutex<Vec<i32>>,
callback_runner: &'a CallbackRunner,
}
// Hypothetical FFI Struct///
struct CallbackRunner {
callback: extern "C" fn(*const Mutex<Vec<i32>>),
data: *const Mutex<Vec<i32>>,
}
impl CallbackRunner {
extern "C" fn default_callback(_vec: *const Mutex<Vec<i32>>) {}
fn new() -> Self {
CallbackRunner {
callback: Self::default_callback,
data: std::ptr::null(),
}
}
fn set_callback(
&mut self, callback: extern "C" fn(*const Mutex<Vec<i32>>),
data: *const Mutex<Vec<i32>>
) {
self.data = data;
self.callback = callback;
}
fn run(&self) {
(self.callback)(self.data);
}
}
/////////////////////////////
impl<'a> WeirdThing<'a> {
fn new(runner: &'a mut CallbackRunner) -> Self {
let vec = Mutex::new(Vec::new());
runner.set_callback(Self::callback, &vec as *const Mutex<Vec<i32>>);
WeirdThing { vec, callback_runner: runner }
}
extern "C" fn callback(vec: *const Mutex<Vec<i32>>) {
let vec = unsafe { &*vec };
let mut vec = vec.lock().unwrap();
vec.push(1);
}
fn do_thing(&self) -> Vec<i32> {
self.callback_runner.run();
self.vec.lock().unwrap().clone()
}
}
let mut runner = CallbackRunner::new();
let thing = WeirdThing::new(&mut runner);
debug!("Thing: {:?}", thing.do_thing());
}
我认为当
thing.vec
被调用时 runner.run()
应该有效,因为它是在 thing
仍然存在时被调用的,但是代码会出现恐慌。
我该如何解释这种行为?
thing
is 在调用回调时有效,问题在于将 vec
添加到结构体以及从函数返回它都会 move 它,换句话说,它可以位于另一个内存位置比 callback_runner.data
指向的那个。
由于您正在取消引用悬空指针,因此您的代码包含 UB,幸运的是您遇到了错误,而不是在某个时候出现静默失败。
要解决这个问题,您必须确保在创建指向它的指针后,
vec
不会在内存中移动,例如通过(固定)Box
将其放在堆上。