Panic! At the Result<T, E>
On letting Rust programs die when they have to
2022-07-25
I’ve been getting back into Rust again lately and I think I overcame a minor but annoying hurdle in understanding what kinds of usage the compiler actually encourages.
In previuous attempts at using Rust I always assumed that a Result<T, E>
always needs to be handled with a match
to cover both the Ok<T>
branch as well as the Err<E>
one. This led to pretty cumbersome code and the alternative was to use .unwrap()
or .expect(msg: &str)
but based on my previous assumption this felt like a “dirty” way of doing it because if the operation that yielded the Result<T, E>
actually failed, the program would panic and terminate.
But here’s the thing, when handling this with a match
in many cases I would do something like this:
fn read_notes() -> String {
let res = fs::read_to_string("notes.txt");
match (res) {
Ok(s) => s,
Err(e) => {
println!("Error reading notes: {}", e);
std::process::exit(1);
}
}
}
$ cargo run
Compiling panic v0.1.0 (/Users/esther/Desktop/temp/panic)
Finished dev [unoptimized + debuginfo] target(s) in 0.18s
Running `target/debug/panic`
Error reading notes: No such file or directory (os error 2)
Now that’s not really that different from letting the program panic:
fn read_notes() -> String {
fs::read_to_string("notes.txt").expect("Error reading notes")
}
$ cargo run
Compiling panic v0.1.0 (/Users/esther/Desktop/temp/panic)
Finished dev [unoptimized + debuginfo] target(s) in 0.21s
Running `target/debug/panic`
thread 'main' panicked at 'Error reading notes: Os { code: 2, kind: NotFound, message: "No such file or directory" }', src/main.rs:4:35
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
The main drawback here is less control over how the error is printed out but that is very often not relevant for tools I write for myself. The important information is still there. But it is much shorter and easier to read and understand, especially when the program does a few things in sequence that all yield a Result<T, E>
.
I actually want my programs to terminate on errors that I havent explicitly coded a recovery path for. This is why I also always do set -e
in Bash scripts. Even something that runs as a server or daemon can and should do this so the error is properly logged and the program can be restarted by something like launchd
or systemd
.
So, I’ll embrace the panic some more, where appropriate, and have a much easier time making progress with Rust.
🦀🦀🦀🦀