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.
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>>
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();
});