我有下面的代码从C代码调用的线程过程。context
是指向堆(Box::into_raw(Box::new(...))
上的结构体的指针。闭包被传递给add_callback
函数,并存储在回调向量中,以便稍后从另一个函数调用。如何修改闭包中app_context
的字段?
#[no_mangle]
pub unsafe extern "C" fn worker_thread_proc(context: *mut c_void) -> c_int {
unsafe {
let app_context = context as *mut AppContext;
let my_closure = Rc::new(|a: N, _, _, _| {
(*app_context).foo = 0; // HOW TO DO THIS PROPERLY?
0
});
// save the closure to a Vec in an object
...
}
}
尝试直接编码会导致错误,这是可以理解的,因为app_context
被闭包借用。
error[E0597]: `app_context` does not live long enough
...
}
- `app_context` dropped here while still borrowed
然而,我知道即使在worker_thread_proc
函数完成之后,raw指针指向的对象仍然有效。在这种情况下,我怎样才能达到修改上下文结构状态的预期目标呢?
我试过在指针上使用Rc
并在使用前克隆它,但这也会导致does not live long enough
错误。我相信也应该有一些方法可以通过Box
来实现,但我自己也无法弄清楚。
更新:增加了一个MRE
use std::ffi::c_void;
use std::ptr;
use std::rc::Rc;
struct AppContext {
ctx: *mut Ctx,
foo: u32
}
pub struct Native(pub Rc<dyn Fn(N, N, N, N) -> N>);
pub enum N {
FuncNativeDef(Native),
Unit
}
pub struct Ctx {
pub values: Vec<N>
}
impl Ctx {
pub fn new() -> Ctx {
Ctx {
values: Vec::new()
}
}
#[inline(always)]
pub fn set_val(&mut self, name: &str, n: N) {
self.values.push(n);
}
}
#[no_mangle]
pub unsafe extern "C" fn worker_thread_proc(context: *mut c_void) {
unsafe {
let app_context = context as *mut AppContext;
let mut ctx = &mut *(*app_context).ctx;
let my_closure = Rc::new(|a: N, _, _, _| {
(*app_context).foo = 2;
N::Unit
});
ctx.set_val("my_closure", N::FuncNativeDef(Native(my_closure)));
}
}
fn main() {
let context = Box::new(AppContext{
foo: 42,
ctx: Box::into_raw(Box::new(Ctx::new()))
});
let context_ptr = Box::into_raw(context);
}
1条答案
按热度按时间oknwwptz1#
一个更小的例子演示了同样的问题:
问题是,要解引用一个指针,你所需要的只是一个对它的引用,但这意味着闭包可以(因此将)通过引用获取
app_context
,这将导致编译器错误,为了解决这个问题,你可以使用
move
关键字强制编译器复制指针app_context
:不过,你得非常小心。移动这个结构体,这是Rust中的默认值,它将悬挂
app_context
指针,然后调用闭包将调用UB!