我正在开发一个应用程序,可选使用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库需要在启动时传递给application.connect_activate
的Fn回调,所以我不能使用FnOnce或FnMut闭包来移动回调中的glib::Receiver。编译器抛出以下错误:
error[E0507]: cannot move out of `window_rx`, a captured variable in an `Fn` closure
我试图通过在Rc中包装window_rx
来避免移动,即:
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,所以如果不使用GUI,我希望完全避免使用GTK和回调。是否有某种方法我可以安全地移动window_rx
闭包内或在回调中解引用它而不会导致错误?
当您需要从类型系统(但在实践中不是)可以多次调用的代码中移出一个值时,可以使用的简单工具是Option
。将值包装在Option
中允许它与Option::None
交换。
当你需要一些东西是可变的,即使你在Fn
内部,你需要内部可变性;在这种情况下,Cell
就可以了。下面是一个完整的可编译程序,它近似于您的情况:
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
包装器(并处理在这种情况下通过panic调用函数两次的可能性)。
应用到你原来的问题,这将是:
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
。