如何在gtk-rs的两个闭包中共享window_id_output变量?
解决方案:在gtk-rs闭包间共享可变状态
问题描述
我在gtk-rs的输入元素中监听用户输入,input.connect_changed会在输入内容变化时触发,input.connect_activate会在按下回车键时触发。我需要在connect_changed闭包中获取xdotool搜索到的窗口ID,然后在connect_activate闭包中用这个ID执行窗口激活命令,但原代码里两个闭包无法共享变量,请问该如何实现?
原代码:
use gtk::prelude::*; use gtk::{Application, ApplicationWindow}; use std::process::{Command, Output}; fn main() { let app = Application::builder() .application_id("com.jwestall.ui-demo") .build(); app.connect_activate(build_ui); app.run(); } fn run_command(command: &str) -> Output { Command::new("sh") .arg("-c") .arg(command) .output() .unwrap_or_else(|_| panic!("failed to execute {}'", command)) } fn build_ui(app: &Application) { let input = gtk::Entry::builder() .placeholder_text("input") .margin_top(12) .margin_bottom(12) .margin_start(12) .margin_end(12) .build(); let window = ApplicationWindow::builder() .application(app) .title("gtk-app") .child(&input) .build(); window.show_all(); input.connect_changed(|entry| { let input_text = entry.text(); let command = format!("xdotool search --onlyvisible --name {}", input_text); let window_id_output = run_command(&command); if window_id_output.status.success() { println!( "stdout: {}", String::from_utf8_lossy(&window_id_output.stdout) ); } else { println!( "sterr: {}", String::from_utf8_lossy(&window_id_output.stderr) ); } }); input.connect_activate(move |entry| { let input_text = entry.text(); // // `xdotool windowactivate` doesn't produce any output let command = format!("xdotool windowactivate {}", window_id_output); let window_activate_output = run_command(&command); println!("window_activate: {}", window_activate_output); window.hide(); window.close(); }); }
实现方案
要在两个闭包间共享可变状态,可使用Rc<RefCell<T>>组合:Rc提供引用计数实现共享所有权,RefCell提供内部可变性,让你在不可变引用的上下文中修改内部值。具体步骤如下:
- 引入必要的模块:
std::cell::RefCell和std::rc::Rc - 创建共享的可变变量存储窗口ID(用
Option<String>处理未找到窗口的情况) - 在
connect_changed闭包中克隆Rc实例,更新内部的窗口ID - 在
connect_activate闭包中克隆Rc实例,读取并使用存储的窗口ID
修改后的完整代码:
use gtk::prelude::*; use gtk::{Application, ApplicationWindow}; use std::cell::RefCell; use std::process::{Command, Output}; use std::rc::Rc; fn main() { let app = Application::builder() .application_id("com.jwestall.ui-demo") .build(); app.connect_activate(build_ui); app.run(); } fn run_command(command: &str) -> Output { Command::new("sh") .arg("-c") .arg(command) .output() .unwrap_or_else(|_| panic!("failed to execute '{}'", command)) } fn build_ui(app: &Application) { let input = gtk::Entry::builder() .placeholder_text("input") .margin_top(12) .margin_bottom(12) .margin_start(12) .margin_end(12) .build(); let window = ApplicationWindow::builder() .application(app) .title("gtk-app") .child(&input) .build(); window.show_all(); // 创建共享的可变状态,存储窗口ID(Option处理未找到的情况) let window_id = Rc::new(RefCell::new(None::<String>)); // 克隆Rc用于connect_changed闭包 let window_id_clone = Rc::clone(&window_id); input.connect_changed(move |entry| { let input_text = entry.text(); let command = format!("xdotool search --onlyvisible --name {}", input_text); let window_id_output = run_command(&command); if window_id_output.status.success() { let stdout = String::from_utf8_lossy(&window_id_output.stdout).trim().to_string(); println!("stdout: {}", stdout); // 更新共享的窗口ID *window_id_clone.borrow_mut() = Some(stdout); } else { let stderr = String::from_utf8_lossy(&window_id_output.stderr); println!("stderr: {}", stderr); // 未找到窗口时清空ID *window_id_clone.borrow_mut() = None; } }); // 克隆Rc用于connect_activate闭包 let window_id_clone = Rc::clone(&window_id); input.connect_activate(move |_| { // 读取共享的窗口ID if let Some(id) = &*window_id_clone.borrow() { let command = format!("xdotool windowactivate {}", id); let window_activate_output = run_command(&command); // 由于windowactivate无输出,打印状态即可 println!("window_activate status: {}", window_activate_output.status.success()); } else { println!("No valid window ID found"); } window.hide(); window.close(); }); }
关键说明
Rc<RefCell<Option<String>>>:Rc让两个闭包都能持有变量的引用,RefCell允许在闭包的不可变环境中修改内部值,Option处理未搜索到窗口的边界情况- 每次连接信号时都需要克隆
Rc,因为每个闭包需要自己的引用计数实例 - 修改
RefCell内部值时使用borrow_mut(),读取时使用borrow(),这两个方法会在运行时检查借用规则,避免数据竞争
内容的提问来源于stack exchange,提问作者wyc
相关产品推荐
相关产品推荐

