rustcursive

Borrowing same item in multiple callbacks


Answer

Deleting import use std::ops::BorrowMut fixed my problem

The Rust Analyzer VS-Code extension automatically imported std::ops::BorrowMut at some point during my many trials at figuring this out. This completely stopped me from using the borrow_mut() of RefCell, and following any tutorials on the subject. This was pointed out by a user in the comments, but never posted as an official answer. I selected the earliest answer that demonstrated RefCell could indeed function as I expected and there was something wrong with my setup.

Original Question

I really would like to have a representation of my app state and be able to pause the TUI library cursive to call other command line programs and then resume.

The callbacks for cursive's add_global_callback are static lifetimes (not sure if they are declared this way, or if cursive is just representing how rust makes callbacks static).

I can't seem to use any combination of Rc, RefCell, RwLock, Mutex, Arc, Box, and Weak that I can think of to pass in a reference to app to multiple callbacks. They only way I succeed is if app is copied or cloned and then the state or the original app is not mutated.

The various errors I get are all about the closure outliving the borrow of app or any kind of app_ptr (Rc, Mutex, RefCell) that I create.

closure may outlive the current function, but it borrows *app_ptr, which is owned by the current function may outlive borrowed value *app_ptr

How can I have a single, static, mutable piece of data in Rust?

#[derive(PartialEq, Debug, Clone, Copy)]
enum AppState {
    RUNNING,
    PATCHING,
    QUITTING
}
#[derive(Clone, Copy)]
struct App {
    state: AppState,
}
impl App {
    pub fn stop(&mut self) {
        self.state = AppState::QUITTING;
    }
    pub fn patch(&mut self) {
        self.state = AppState::PATCHING;
    }
    pub fn resume(&mut self) {
        self.state = AppState::RUNNING;
    }
    pub fn is_running(&mut self) -> bool {
        return self.state != AppState::QUITTING;
    }
}
fn main() {
    let mut app = App{state: AppState::RUNNING};
    let mut siv = cursive::default();

    let mut app_ptr = Box::new(app);
    siv.add_global_callback(Key::Esc, |s| {
        app_ptr.stop();
        s.quit();
    });
    siv.add_global_callback(Event::Char('q'), |s| {
        app_ptr.stop();
        s.quit();
    });
    siv.add_global_callback(Event::Char('p'), |s| {
        app_ptr.patch();
        s.quit();
    });

    while app.state != AppState::QUITTING {
        siv.run();
        if app.state == AppState::PATCHING {
            siv.clear();
            let mut child = Command::new("git")
                .arg("add")
                .arg("-p")
                .stdin(std::process::Stdio::inherit())
                .stdout(std::process::Stdio::inherit())
                .spawn()
                .expect("failed to execute child proc");
            match child.wait() {
                Ok(_) => (),
                Err(_) => eprintln!("Could not execute get add patch correctly."),
            }
        }
    }
}

I've tried the Rc::new(RefCell::new(app)) trick that i've seen a few places, but the compiler tells me that type Rc<RefCell> does not have a method stop()

    let mut app = App{state: AppState::RUNNING};
    let mut siv = cursive::default();
    let mut app_ptr = Rc::new(RefCell::new(app));
    {
        let app_ptr_for_esc = Rc::clone(&app_ptr);
        siv.add_global_callback(Key::Esc, move |s| {
            app_ptr_for_esc.borrow_mut().stop();
            s.quit();
        });
    }

no method named stop found for mutable reference &mut Rc<RefCell<App>> in the current scope method not found in &mut Rc<RefCell<App>>


Solution

  • I can't seem to use any combination of Rc, RefCell, RwLock, Mutex, Arc, Box, and Weak that I can think of to pass in a reference to app to multiple callbacks. T

    I don't know what exactly you've tried, but Rc<RefCell<App>> compiles for me:

    let app = Rc::new(RefCell::new(App{state: AppState::RUNNING}));
    let mut siv = cursive::default();
    
    let app_for_esc = app.clone();
    let app_for_q = app.clone();
    let app_for_p = app.clone();
    
    siv.add_global_callback(Key::Esc, move |s| {
        app_for_esc.borrow_mut().stop();
        s.quit();
    });
        
    siv.add_global_callback(Event::Char('q'), move |s| {
        app_for_q.borrow_mut().stop();
        s.quit();
    });
    siv.add_global_callback(Event::Char('p'), move |s| {
        app_for_p.borrow_mut().patch();
        s.quit();
    });
    

    Rustexplorer.