-1

我正在使用 ImGui Rust 绑定,我有一个大问题:我需要从另一个线程访问窗口的“系统”变量:这个变量授予对几个函数的控制权,例如更改窗口大小。我需要在.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)必须使用“移动”来传递所有变量的所有权,因此它们本质上需要静态生命周期。

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

因为我也需要一个全局状态,所以最好将此变量包装在一个结构中。我设法使结构可以毫无问题地传递给线程,但“系统”变量仍然拒绝工作。

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

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| {});
}

这不起作用,原因如下:

Arc由于 value 具有 type std::option::Option<support::System>,它没有实现 Copytraitrustc(E0507) main.rs(31, 5) 导致无法移出move 发生:考虑借用Option的内容

借款是行不通的,所以这肯定很烦人。然后我们只需在结构中存储对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借来的价值不够长 生存时间不够长rustc(E0597) main.rs(59, 1):system在仍然借用的时候丢到这里 main.rs(25, 17): 参数要求system借用'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 **system1which is behind a shared reference move 因为**system1有类型support::System,它没有实现Copytraitrustc(E0507)

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

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

我还想补充一点,我通过将“状态”包装在Arc<Mutex<>>中尝试了相同的Arc<>场景。它会导致示例触发的相同错误之一。我还尝试使用该结构的静态版本,但这会产生它不可变的问题,并且我相信它会引发相同的所有权错误。

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

我希望有经验的 Rustacean 知道解决这个问题!提前非常感谢。

4

1 回答 1

-1

我发现我看错了方向:我试图从.build(ui, || {})块中的main()顶部访问内容,但我应该做的是从system.main_loop()作为参数。仍然应该像我之前尝试的那样完成具有微不足道变量的全局状态,但是当您需要对窗口内部的引用时,您确实需要一个不同的解决方案。

我进行了以下更改: 在 mod.rs 中(此文件包含在链接的 ImGui 示例中),更改main_loop()中F的签名以传递&Displaypub 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以接收这个新参数。然后,您可以使用窗口上下文做令人兴奋的事情!

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
});

感谢您的评论家伙,我希望这可以帮助其他人!

于 2020-07-31T13:22:36.727 回答