You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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>。

具体修改步骤:

  1. 将input1和but_cal1用Rc<RefCell<Input>>、Rc<RefCell<Button>>包装,并克隆出供各个回调使用的指针。
  2. 在回调内部,通过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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 11:04:57