rust 如何通过原始指针修改闭包中的结构体字段?

3lxsmp7m  于 2023-10-20  发布在  其他
关注(0)|答案(1)|浏览(98)

我有下面的代码从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);
}
oknwwptz

oknwwptz1#

一个更小的例子演示了同样的问题:

use std::ffi::c_void;

struct AppContext {
    ctx: Option<Box<dyn Fn()>>,
    foo: u32,
}

#[no_mangle]
pub unsafe extern "C" fn worker_thread_proc(context: *mut c_void) {
    let app_context = context as *mut AppContext;
    let ctx = unsafe { &mut (*app_context).ctx };

    let my_closure = Box::new(|| {
        unsafe { (*app_context).foo = 2 };
    });

    *ctx = Some(my_closure);
}

问题是,要解引用一个指针,你所需要的只是一个对它的引用,但这意味着闭包可以(因此将)通过引用获取app_context,这将导致编译器错误,

error[E0597]: `app_context` does not live long enough
  --> src/main.rs:14:18
   |
13 |     let my_closure = Box::new(|| {
   |                              -- value captured here
14 |         unsafe { (*app_context).foo = 2 };
   |                  ^^^^^^^^^^^^^^^^^^ borrowed value does not live long enough
...
17 |     *ctx = Some(my_closure);
   |                 ---------- cast requires that `app_context` is borrowed for `'static`
18 | }
   | - `app_context` dropped here while still borrowed

为了解决这个问题,你可以使用move关键字强制编译器复制指针app_context

let my_closure = Box::new(move || { /*…*/ });

不过,你得非常小心。移动这个结构体,这是Rust中的默认值,它将悬挂app_context指针,然后调用闭包将调用UB!

相关问题