将不可克隆的非静态对象传递给需要静态数据的线程
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
});
感谢您的评论,希望这对以后的其他人有所帮助!
我正在使用 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 typestd::option::Option<support::System>
, which does not implement theCopy
traitrustc(E0507) main.rs(31, 5): consider borrowing theOption
'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 thatsystem
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 typesupport::System
, which does not implement theCopy
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
});
感谢您的评论,希望这对以后的其他人有所帮助!