在 Fn 中安全地移动或取消引用 Receiver?
Safely move or dereference Receiver in a Fn?
我正在开发一个 可选 使用 GUI 来显示大致结构如下的视频数据的应用程序:
fn main() {
let (window_tx, window_rx) =
MainContext::channel::<MyStruct>(PRIORITY_DEFAULT);
let some_thread = thread::spawn(move || -> () {
// send data to window_tx
});
let application =
gtk::Application::new(Some("com.my.app"), Default::default());
application.connect_activate(move |app: >k::Application| {
build_ui(app, window_rx);
});
application.run();
some_thread.join().unwrap();
}
fn build_ui(application: >k::Application, window_rx: Receiver<MyStruct>) {
window_rx.attach( ... );
}
gtk rust 库需要在启动时将 Fn 回调传递给 application.connect_activate
,因此我无法使用 FnOnce 或 FnMut 闭包在回调中移动 glib::Receiver。编译器抛出此错误:
error[E0507]: cannot move out of `window_rx`, a captured variable in an `Fn` closure
我试图通过将 window_rx
包装在 Rc 中来避免移动,即:
let r = Rc::new(RefCell::new(window_rx));
application.connect_activate(move |app: >k::Application| {
build_ui(app, Rc::clone(&r));
});
但是在我的 build_ui
函数中取消引用 Rc 时,我得到了这个错误:
error[E0507]: cannot move out of an `Rc`
到目前为止我使用的回退方法是将通道创建和线程创建移到我的 build_ui
函数中,但是因为不需要 GUI,我希望避免使用 GTK 和回调如果不使用 GUI,则完全如此。有什么方法可以安全地在闭包中移动 window_rx
或者在回调中取消引用它而不会导致错误?
当您需要从代码中移出一个值时,通过类型系统而不是在实践中,可以多次调用,可以使用的简单工具是 Option
。将值包装在 Option
中允许它 交换 与 Option::None
.
当您需要某些东西即使在 Fn
中也是可变的时,您需要内部可变性;在这种情况下,Cell
就可以了。这是 a complete compilable program 最接近您的情况:
use std::cell::Cell;
// Placeholders to let it compile
use std::sync::mpsc;
fn wants_fn_callback<F>(_f: F) where F: Fn() + 'static {}
struct MyStruct;
fn main() {
let (_, window_rx) = mpsc::channel::<MyStruct>();
let window_rx: Cell<Option<mpsc::Receiver<MyStruct>>> = Cell::new(Some(window_rx));
wants_fn_callback(move || {
let _: mpsc::Receiver<MyStruct> = window_rx.take().expect("oops, called twice");
});
}
Cell::take()
从 Cell
中删除 Option<Receiver>
,留下 None
。 expect
然后删除 Option
包装器(并在这种情况下通过恐慌处理函数被调用两次的可能性)。
应用于您的原始问题,这将是:
let window_rx: Option<Receiver<MyStruct>> = Cell::new(Some(window_rx));
application.connect_activate(move |app: >k::Application| {
build_ui(app, window_rx.take().expect("oops, called twice"));
});
但是,请注意:如果库需要 Fn
闭包,则在某些情况下可能会多次调用该函数,在这种情况下,您应该准备好在那种情况。如果没有这种情况,那么库的 API 应该改进为 FnOnce
代替。
我正在开发一个 可选 使用 GUI 来显示大致结构如下的视频数据的应用程序:
fn main() {
let (window_tx, window_rx) =
MainContext::channel::<MyStruct>(PRIORITY_DEFAULT);
let some_thread = thread::spawn(move || -> () {
// send data to window_tx
});
let application =
gtk::Application::new(Some("com.my.app"), Default::default());
application.connect_activate(move |app: >k::Application| {
build_ui(app, window_rx);
});
application.run();
some_thread.join().unwrap();
}
fn build_ui(application: >k::Application, window_rx: Receiver<MyStruct>) {
window_rx.attach( ... );
}
gtk rust 库需要在启动时将 Fn 回调传递给 application.connect_activate
,因此我无法使用 FnOnce 或 FnMut 闭包在回调中移动 glib::Receiver。编译器抛出此错误:
error[E0507]: cannot move out of `window_rx`, a captured variable in an `Fn` closure
我试图通过将 window_rx
包装在 Rc 中来避免移动,即:
let r = Rc::new(RefCell::new(window_rx));
application.connect_activate(move |app: >k::Application| {
build_ui(app, Rc::clone(&r));
});
但是在我的 build_ui
函数中取消引用 Rc 时,我得到了这个错误:
error[E0507]: cannot move out of an `Rc`
到目前为止我使用的回退方法是将通道创建和线程创建移到我的 build_ui
函数中,但是因为不需要 GUI,我希望避免使用 GTK 和回调如果不使用 GUI,则完全如此。有什么方法可以安全地在闭包中移动 window_rx
或者在回调中取消引用它而不会导致错误?
当您需要从代码中移出一个值时,通过类型系统而不是在实践中,可以多次调用,可以使用的简单工具是 Option
。将值包装在 Option
中允许它 交换 与 Option::None
.
当您需要某些东西即使在 Fn
中也是可变的时,您需要内部可变性;在这种情况下,Cell
就可以了。这是 a complete compilable program 最接近您的情况:
use std::cell::Cell;
// Placeholders to let it compile
use std::sync::mpsc;
fn wants_fn_callback<F>(_f: F) where F: Fn() + 'static {}
struct MyStruct;
fn main() {
let (_, window_rx) = mpsc::channel::<MyStruct>();
let window_rx: Cell<Option<mpsc::Receiver<MyStruct>>> = Cell::new(Some(window_rx));
wants_fn_callback(move || {
let _: mpsc::Receiver<MyStruct> = window_rx.take().expect("oops, called twice");
});
}
Cell::take()
从 Cell
中删除 Option<Receiver>
,留下 None
。 expect
然后删除 Option
包装器(并在这种情况下通过恐慌处理函数被调用两次的可能性)。
应用于您的原始问题,这将是:
let window_rx: Option<Receiver<MyStruct>> = Cell::new(Some(window_rx));
application.connect_activate(move |app: >k::Application| {
build_ui(app, window_rx.take().expect("oops, called twice"));
});
但是,请注意:如果库需要 Fn
闭包,则在某些情况下可能会多次调用该函数,在这种情况下,您应该准备好在那种情况。如果没有这种情况,那么库的 API 应该改进为 FnOnce
代替。