While playing with tui
, I tried to catch panics in order to restore terminal operation if a panic occurs in my problem. But I get strange behavior. This is what I made:
[dependencies]
tui = "0.18.0"
crossterm = "0.23.2"
use crossterm::execute;
use crossterm::terminal::{
disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen,
};
use tui::{backend::CrosstermBackend, Terminal};
use std::io;
use std::panic;
use std::thread::sleep;
use std::time::Duration;
fn run() -> Result<(), io::Error> {
sleep(Duration::from_secs(1));
panic!("PANIC!");
}
fn main() -> Result<(), io::Error> {
enable_raw_mode()?;
let mut stdout = io::stdout();
execute!(stdout, EnterAlternateScreen)?;
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?;
let result = panic::catch_unwind(|| run().unwrap());
disable_raw_mode()?;
execute!(terminal.backend_mut(), LeaveAlternateScreen)?;
terminal.show_cursor()?;
if let Err(err) = result {
println!("This is executed.");
sleep(Duration::from_secs(1));
panic::resume_unwind(err);
println!("This is not executed, but where went the panic output?");
}
Ok(())
}
When I execute cargo run
, the screen gets empty for 1 second, and then all I get is the following output:
% cargo run
warning: unreachable statement
--> src/main.rs:31:9
|
30 | panic::resume_unwind(err);
| ------------------------- any code following this expression is unreachable
31 | println!("This is not executed, but where went the panic output?");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unreachable statement
|
= note: `#[warn(unreachable_code)]` on by default
= note: this warning originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
warning: `mycrate` (bin "mycrate") generated 1 warning
Finished dev [unoptimized + debuginfo] target(s) in 0.01s
Running `target/debug/mycrate`
This is executed.
But where did my error end up?
If I instead move resume_unwind
up, it kinda works (but gets messed up due to the bad terminal state):
fn main() -> Result<(), io::Error> {
enable_raw_mode()?;
let mut stdout = io::stdout();
execute!(stdout, EnterAlternateScreen)?;
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?;
let result = panic::catch_unwind(|| run().unwrap());
disable_raw_mode()?;
+ if let Err(err) = result {
+ println!("This is executed.");
+ sleep(Duration::from_secs(1));
+ panic::resume_unwind(err);
+ println!("This is not executed, but where went the panic output?");
+ }
execute!(terminal.backend_mut(), LeaveAlternateScreen)?;
terminal.show_cursor()?;
- if let Err(err) = result {
- println!("This is executed.");
- sleep(Duration::from_secs(1));
- panic::resume_unwind(err);
- println!("This is not executed, but where went the panic output?");
- }
Ok(())
}
Output:
thread 'main' panicked at 'PANIC!', src/main.rs:14:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
This is executed.
My questions:
- Why doesn't
resume_unwind
work as expected in the first case (i.e. when I call it after restoring the terminal) by showing the panic? - Is it reasonable to use
catch_unwind
, or should I rather implement some sort of drop guard?