Rust中FLTK库多回调调用同一组件的所有权问题求助
问题
我正在用Rust的FLTK GUI库开发一款基础软件,在第18行创建了名为input1的输入框组件,需要在两个不同的回调函数中调用它,但第一个回调获取了该组件的所有权,导致在第41行创建第二个回调时出现错误。请问该如何正确创建该组件并在不同回调中调用它?
原代码如下:
use std::{borrow::BorrowMut, rc::Rc, cell::RefCell, sync::{Arc, Mutex}}; use fltk::{*, app, button::{Button, LightButton}, frame::Frame, prelude::*, window::Window, enums::*, text::{TextBuffer, TextDisplay}}; use fltk_calendar::calendar; use fltk::button::CheckButton; use chrono::prelude::*; fn main() { let mut data_geracao_str = String::new(); let mut data_geracao_inicio_str = String::new(); let app = app::App::default(); let mut wind = Window::new(20, 40, 800, 600, "Gerador de Arquivos Diários - Armarinhos Fernando"); let mut _titulo1 = Frame::new(30, 33, 150, 10, "Data Inicial"); let mut input1 = input::Input::new(160,25,150,35,""); let mut but_cal1 = button::Button::new(320, 25, 80, 36, "Calendário"); but_cal1.set_label_size(10); input1.deactivate(); but_cal1.deactivate(); but_cal1.set_callback( move|_| { let cal = calendar::Calendar::default(); let naive_date = cal.get_date(); if let Some(naive_date) = naive_date { data_geracao_inicio_str.clear(); data_geracao_inicio_str.push_str(naive_date.format("%Y-%m-%d").to_string().as_str()); input1.set_value(data_geracao_inicio_str.as_str()); data_geracao_inicio_str.push_str(" 00:00:00 +00:00"); println!("{:?}",&data_geracao_inicio_str); } }); let mut check_input = CheckButton::new(415,25,250,40,"Data Inicio Diferente"); check_input.set_callback( |check1|{ if check1.is_checked(){ input1.set_value(""); //input1.set_value(""); //&input1.activate(); but_cal1.activate(); }else{ //&input1.set_value(""); //&input1.deactivate(); but_cal1.deactivate(); } }); let mut _titulo2 = Frame::new(30, 95, 150, 10, "Data Final"); let mut input2 = input::Input::new(160,85,150,35,""); let mut but_cal2 = button::Button::new(320, 85, 80, 36, "Calendário"); but_cal2.set_label_size(10); let mut btn_pix = button::Button::new(30, 200, 110, 36, "PIX"); let mut btn_vendas = button::Button::new(150, 200, 110, 36, "Vendas"); let mut btn_previsao = button::Button::new(270, 200, 110, 36, "Previsão"); let mut btn_recebimento = button::Button::new(390, 200, 110, 36, "Recebimento"); let mut btn_ajustes = button::Button::new(510, 200, 110, 36, "Ajustes"); but_cal2.set_callback(move |_| { let cal = calendar::Calendar::default(); let naive_date = cal.get_date(); if let Some(naive_date) = naive_date { data_geracao_str.clear(); data_geracao_str.push_str(naive_date.format("%Y-%m-%d").to_string().as_str()); input2.set_value(data_geracao_str.as_str()); data_geracao_str.push_str(" 00:00:00 +00:00"); println!("{:?}",data_geracao_str); } }); wind.end(); wind.show(); app.run().unwrap(); }
解决方案
在Rust中,要让多个回调共享同一GUI组件的访问权,需要使用引用计数+内部可变性的组合——Rc<RefCell<T>>。FLTK是单线程GUI框架,不需要跨线程同步,所以用Rc(线程不安全的引用计数)搭配RefCell(运行时检查的内部可变性)就足够了,无需Arc<Mutex>。
具体修改步骤:
- 将
input1和but_cal1用Rc<RefCell<Input>>、Rc<RefCell<Button>>包装,并克隆出供各个回调使用的指针。 - 在回调内部,通过
RefCell的borrow_mut()方法获取组件的可变引用,从而执行修改操作。
修改后的完整代码:
use std::{rc::Rc, cell::RefCell}; use fltk::{*, app, button::{Button, CheckButton}, frame::Frame, prelude::*, window::Window}; use fltk_calendar::calendar; use chrono::prelude::*; fn main() { let mut data_geracao_str = String::new(); let mut data_geracao_inicio_str = String::new(); let app = app::App::default(); let mut wind = Window::new(20, 40, 800, 600, "Gerador de Arquivos Diários - Armarinhos Fernando"); let _titulo1 = Frame::new(30, 33, 150, 10, "Data Inicial"); // 用Rc<RefCell>包装input1和but_cal1 let input1 = Rc::new(RefCell::new(input::Input::new(160,25,150,35,""))); let but_cal1 = Rc::new(RefCell::new(button::Button::new(320, 25, 80, 36, "Calendário"))); but_cal1.borrow_mut().set_label_size(10); input1.borrow_mut().deactivate(); but_cal1.borrow_mut().deactivate(); // 克隆指针供第一个回调使用 let input1_clone = Rc::clone(&input1); let mut data_geracao_inicio_str_clone = data_geracao_inicio_str.clone(); but_cal1.borrow_mut().set_callback(move|_| { let cal = calendar::Calendar::default(); let naive_date = cal.get_date(); if let Some(naive_date) = naive_date { data_geracao_inicio_str_clone.clear(); data_geracao_inicio_str_clone.push_str(naive_date.format("%Y-%m-%d").to_string().as_str()); input1_clone.borrow_mut().set_value(data_geracao_inicio_str_clone.as_str()); data_geracao_inicio_str_clone.push_str(" 00:00:00 +00:00"); println!("{:?}",&data_geracao_inicio_str_clone); } }); let check_input = CheckButton::new(415,25,250,40,"Data Inicio Diferente"); // 克隆指针供第二个回调使用 let input1_clone2 = Rc::clone(&input1); let but_cal1_clone = Rc::clone(&but_cal1); check_input.set_callback(move |check1|{ if check1.is_checked(){ input1_clone2.borrow_mut().set_value(""); but_cal1_clone.borrow_mut().activate(); }else{ but_cal1_clone.borrow_mut().deactivate(); } }); let _titulo2 = Frame::new(30, 95, 150, 10, "Data Final"); let mut input2 = input::Input::new(160,85,150,35,""); let mut but_cal2 = button::Button::new(320, 85, 80, 36, "Calendário"); but_cal2.set_label_size(10); let btn_pix = button::Button::new(30, 200, 110, 36, "PIX"); let btn_vendas = button::Button::new(150, 200, 110, 36, "Vendas"); let btn_previsao = button::Button::new(270, 200, 110, 36, "Previsão"); let btn_recebimento = button::Button::new(390, 200, 110, 36, "Recebimento"); let btn_ajustes = button::Button::new(510, 200, 110, 36, "Ajustes"); let mut data_geracao_str_clone = data_geracao_str.clone(); but_cal2.set_callback(move |_| { let cal = calendar::Calendar::default(); let naive_date = cal.get_date(); if let Some(naive_date) = naive_date { data_geracao_str_clone.clear(); data_geracao_str_clone.push_str(naive_date.format("%Y-%m-%d").to_string().as_str()); input2.set_value(data_geracao_str_clone.as_str()); data_geracao_str_clone.push_str(" 00:00:00 +00:00"); println!("{:?}",data_geracao_str_clone); } }); wind.end(); wind.show(); app.run().unwrap(); }
关键说明
Rc<T>允许同一数据拥有多个所有者,每次克隆都会增加引用计数,当计数为0时数据才会被释放。RefCell<T>提供了运行时的可变借用检查,允许在不可变引用的情况下修改内部数据,这正好适配FLTK组件的回调场景(回调闭包通常只能捕获不可变环境,但我们需要修改组件)。- 注意在回调中必须克隆
Rc指针,避免所有权转移导致原变量失效。
内容的提问来源于stack exchange,提问作者Paulo Luvisoto
相关产品推荐
相关产品推荐

