如何在connect_activate外创建glib::MainContext::channel并传入处理?
我是Rust和GTK的新手,希望在初始化gtk::Application之前创建glib::MainContext::channel对,我认为它的作用和std::sync::mpsc完全一致。虽然我能创建并保存rx/tx通道对,但不确定如何将Receiver传入gtk::Application::connect_activate()信号处理器中以调用.attach()。
我修改的示例代码如下:
struct MyData { ready_tx: glib::Sender<i32>, //ready_rx: glib::Receiver<i32>, } fn setup() -> MyData { let (ready_tx, ready_rx) = glib::MainContext::channel(glib::Priority::default()); MyData { ready_tx, //ready_rx, } } fn main() { let data = setup(); // // Setup other threads by cloning data.sender // let application = gtk::Application::new( Some("com.github.gtk-rs.examples.cairo_threads"), Default::default(), ); application.connect_activate(move |app| { build_ui(&app, &data) }); application.run(); } fn build_ui(application: >k::Application, data: &MyData) { ... data.ready_rx.attach( { ... }); }
上述代码(忽略未实现的build_ui)可编译运行,但存在明显问题:ready_rx并未被包含进去。若添加ready_rx,则会在build_ui()中出现错误:move occurs because data.ready_rx has type glib::Receiver
我希望将Receiver<i32>的所有权从setup()函数传递到main,最终被build_ui()函数消费,但不清楚实现方式,也不理解为何需要Copy trait,且我无法克隆Receiver。我需要先创建一些额外线程,这些线程在初始化GTK GUI前就需要Sender的引用,因此想这样实现,但未找到在build_ui()外创建glib::MainContext::channel对的示例,是否有其他方案?
解决方案
感谢@chayim-friedman的解答,修改后的代码如下:
use std::cell::Cell; struct MyData { ready_tx: glib::Sender<i32>, ready_rx: Cell<Option<glib::Receiver<i32>>>, } fn setup() -> MyData { let (ready_tx, ready_rx) = glib::MainContext::channel(glib::Priority::default()); MyData { ready_tx, ready_rx: Cell::new(Some(ready_rx)), } } fn main() { let data = setup(); // // Setup other threads by cloning data.sender // let application = gtk::Application::new( Some("com.github.gtk-rs.examples.cairo_threads"), Default::default(), ); application.connect_activate(move |app| { build_ui(&app, &data) }); application.run(); } fn build_ui(application: >k::Application, data: &MyData) { ... data.ready_rx.take().unwrap().attach( { ... }); }
思路说明
用Cell<Option<glib::Receiver<i32>>>包装Receiver:
Cell允许在持有不可变引用的情况下修改内部值,适配build_ui中对data的不可变引用场景Option的take()方法会取出内部的Receiver并留下None,一次性转移所有权,刚好满足attach()需要消费Receiver所有权的需求,避免了移动语义冲突
内容的提问来源于stack exchange,提问作者awm129

