将不可克隆的非静态对象传递给需要静态数据的线程

Pass non-clonable, non-static object to thread requiring static data

我正在使用 ImGui Rust 绑定,但我遇到了一个大问题: 我需要从另一个线程访问 window 的 'system' 变量:这个变量授予对几个函数的控制,例如改变 window 大小。 我需要在 .build(ui, || { }) 块中访问此变量。此代码取自(但已缩短)示例:https://github.com/Gekkio/imgui-rs/blob/master/imgui-examples/examples/hello_world.rs

use imgui::*;

mod support;

fn main() {
    let system: support::System = support::init(file!());
    system.main_loop(move |_, ui| {
        Window::new(im_str!("Hello world"))
            .size([300.0, 110.0], Condition::FirstUseEver)
            .build(ui, || {
                // Do stuff with the UI here.
            });
    });
}

这是一个问题,因为事件循环函数(即 .build() 函数中的 lambda)必须使用 'move' 来传递所有变量的所有权,因此它们本质上需要静态生命周期.

我遇到过几个几乎有同样问题的人的帖子,但我的数据是非静态的,不可复制的。这就是头痛的地方。

因为我也需要一个全局状态,所以最好将这个变量包装在一个结构中。我设法让它可以毫无问题地传递给线程,但是 'system' 变量仍然拒绝工作。

以下是我尝试过的一些场景示例:

struct State {
    system: Option<support::System>
}

fn main() {
    let state = Arc::new(State {
        system: Some(support::init(file!()))
    });

    ERROR -> state.system.unwrap().main_loop(move |x: &mut bool, ui: &mut Ui| {});
}

这不起作用,原因如下:

cannot move out of an Arc move occurs because value has type std::option::Option<support::System>, which does not implement the Copy traitrustc(E0507) main.rs(31, 5): consider borrowing the Option's content

借钱不行,这可真烦人。然后我们只要在结构中存储对 support::System 的引用,这样就解决了,对吧?

struct State<'a> {
    system: Option<&'a support::System>
}

fn main() {
    let system = support::init(file!()); 
    let state = Arc::new(State {
        ERROR -> system: Some(&system)
    });
}

这行不通,因为 Rust 编译器认为数据不会存在足够长的时间(我知道会存在,但编译器无法正确确定)

system does not live long enough borrowed value does not live long enoughrustc(E0597) main.rs(59, 1): system dropped here while still borrowed main.rs(25, 17): argument requires that system is borrowed for 'static

好的,听起来很有道理。然后只需使用 Box<> 将其分配到堆上并忘记整个问题。

struct State {
    system: Box<support::System>
}

fn main() {
    let state = Arc::new(State {
        system: Box::new(support::init(file!()))
    });

    let state1 = state.clone();
    let system1 = &state1.system;
    ERROR -> system1.main_loop(move |x: &mut bool, ui: &mut Ui| {
        Window::new(im_str!("Main"))
        .flags(WindowFlags::NO_DECORATION | WindowFlags::NO_RESIZE)
        .position([0f32, 0f32], Condition::Always)
        .build(ui, || {
            let state = state.clone();
            THIS WORKS! -> let system = &state.system;
            // Do stuff with the 'system' variable.
        });
    });
}

这会导致类型稍微复杂的错误:

cannot move out of **system1 which is behind a shared reference move occurs because **system1 has type support::System, which does not implement the Copy traitrustc(E0507)

什么?我正在尝试访问参考,我不想复制任何内容。

我没主意了。我在 C++ 中有几乎完全相同的代码并且可以毫无问题地工作,因为 C++ 不关心变量所有权:您可以根据需要传递 'state' 指针。

我还想补充一点,我尝试了相同的 Arc<> 方案,方法是将 'State' 包装在 Arc<互斥体<>>。它会导致示例触发的相同错误之一。我也尝试过使用该结构的静态版本,但这会产生它不可变的问题,我相信它会引发相同的所有权错误。

我觉得这很容易解决,但它确实需要一些(高级)Rust 借用机制方面的经验。我是 Rust 新手(不到一周的业余爱好经验),我可以掌握很多概念,但我本质上需要避免这里的所有权机制,因为数据必须全局共享。

希望有经验的Rustacean知道解决办法!非常感谢。

我发现我看错了方向:我试图从 main() 顶部的 .build(ui, || {}) 块,但我应该做的是从 system.main_loop()[=29 中检索该信息=] 作为参数。具有琐碎变量的全局状态仍应像我之前尝试的那样完成,但是当您需要引用 window 内部结构时,您确实需要不同的解决方案。

我进行了以下更改: 在 mod.rs 中(此文件包含在链接的 ImGui 示例中),在 main_loop 中更改 F 的签名( ) 传递一个 &Display: pub fn main_loop<F: FnMut(&mut bool, &Display, &mut Ui) + 'static>(self, mut run_ui: F)

然后在上述函数中,将调用更改为 run_ui() 以传递显示变量的引用。这个函数已经拥有所有这些内部变量,所以你可以直接访问它:run_ui(&mut run, &display, &mut ui); 这样做之后,您现在可以将 lambda 编辑为 main_loop 以接收此新参数。然后,您可以使用 window 上下文做一些令人兴奋的事情!

system.main_loop(move |x: &mut bool, display: &glium::Display, ui: &mut Ui| {
    let gl_window = display.gl_window();
    let window_size = gl_window.window().inner_size();

    Window::new(im_str!("Main"))
        .position([0f32, 0f32], Condition::Always)
        .size([window_size.width as f32, window_size.height as f32], Condition::Always) // <- We can now use window internals here!
        // etc, build your window as usual
});

感谢您的评论,希望这对以后的其他人有所帮助!