代码之家  ›  专栏  ›  技术社区  ›  rumdrums

安全地移动或取消引用Fn中的接收器?

  •  0
  • rumdrums  · 技术社区  · 3 年前

    我正在开发一个应用程序 可选地 使用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: &gtk::Application| {
            build_ui(app, window_rx); 
        });
    
        application.run();
    
        some_thread.join().unwrap();
    }
    
    fn build_ui(application: &gtk::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: &gtk::Application| {
            build_ui(app, Rc::clone(&r)); 
        });
    
    

    但是在我的 build_ui 函数,我得到这个错误:

    error[E0507]: cannot move out of an `Rc`
    

    到目前为止,我使用的后备方法是将通道创建和线程创建移动到 build_ui 函数,但因为不需要GUI,所以我希望在不使用GUI的情况下完全避免使用GTK和回调。有什么办法可以让我安全地移动吗 window_rx 在闭包中,或者在回调中以其他方式取消引用它而不会导致错误?

    0 回复  |  直到 3 年前
        1
  •  2
  •   Kevin Reid    3 年前

    当您需要从代码中移出一个值时,根据类型系统,但在实践中不能多次调用该值,需要使用的简单工具是 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() 删除 Option<Receiver> 来自 单间牢房 ,离开 None 在它的位置。这个 expect 然后删除 选项 wrapper(并处理在这种情况下函数因恐慌而被调用两次的可能性)。

    应用于您最初的问题,这将是:

        let window_rx: Option<Receiver<MyStruct>> = Cell::new(Some(window_rx));
        application.connect_activate(move |app: &gtk::Application| {
            build_ui(app, window_rx.take().expect("oops, called twice")); 
        });
    

    但是,请小心:如果库需要 Fn 闭包,在某种情况下,函数可能会被多次调用,在这种情况下,您应该准备好在这种情况下做一些适当的事情。如果没有这样的条件,那么应该改进库的API以获得 FnOnce 相反

    推荐文章