rust error handling
Unrecoverable errors with panic!
// print a failure message, unwind and clean up the stack, and then quit
fn main() {
panic!("crash and burn");
}
By default, Rust walks back up the stack and cleans up the data from each function it encounters. Walking back and cleaning up is a lot of work.
You can immediately abort and let the OS clean up by adding the following in the Cargo.toml
:
# example of adding abort on panic in release mode
[profile.release]
panic = 'abort'
We can set the RUST_BACKTRACE
environment variable to get a backtrace of exactly what happened to
cause the error:
$ RUST_BACKTRACE=1 cargo run
thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 99', src/main.rs:4:5
stack backtrace:
0: rust_begin_unwind
at /rustc/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/library/std/src/panicking.rs:483
1: core::panicking::panic_fmt
at /rustc/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/library/core/src/panicking.rs:85
2: core::panicking::panic_bounds_check
at /rustc/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/library/core/src/panicking.rs:62
3: <usize as core::slice::index::SliceIndex<[T]>>::index
at /rustc/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/library/core/src/slice/index.rs:255
4: core::slice::index::<impl core::ops::index::Index<I> for [T]>::index
at /rustc/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/library/core/src/slice/index.rs:15
5: <alloc::vec::Vec<T> as core::ops::index::Index<I>>::index
at /rustc/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/library/alloc/src/vec.rs:1982
6: panic::main
at ./src/main.rs:4
7: core::ops::function::FnOnce::call_once
at /rustc/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/library/core/src/ops/function.rs:227
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
Recoverable errors with Result
// The `Result` enum is defined as having two variants: `Ok` and `Err`
enum Result<T, E> {
Ok(T),
Err(E),
}
use std::fs::File;
fn main() {
let f = File::open("hello.txt");
let f = match f {
Ok(file) => file,
Err(error) => panic!("Problem opening the file: {:?}", error),
};
}
Matching on different errors
use std::fs::File;
use std::io::ErrorKind;
fn main() {
let f = File::open("hello.txt");
let f = match f {
Ok(file) => file,
Err(error) => match error.kind() {
ErrorKind::NotFound => match File::create("hello.txt") {
Ok(fc) => fc,
Err(e) => panic!("Problem creating the file: {:?}", e),
},
other_error => {
panic!("Problem opening the file: {:?}", other_error)
}
},
};
}
// same as above but more concise
use std::fs::File;
use std::io::ErrorKind;
fn main() {
let f = File::open("hello.txt").unwrap_or_else(|error| {
if error.kind() == ErrorKind::NotFound {
File::create("hello.txt").unwrap_or_else(|error| {
panic!("Problem creating the file: {:?}", error);
})
} else {
panic!("Problem opening the file: {:?}", error);
}
});
}
Shortcuts for panic on Error: unwrap and expect
use std::fs::File;
fn main() {
// will call panic! if Err variant
let f = File::open("hello.txt").unwrap();
}
use std::fs::File;
fn main() {
// same as above, but provides a good error message
let f = File::open("hello.txt").expect("Failed to open hello.txt");
}
Propagating Errors
use std::fs::File;
use std::io::{self, Read};
// return the error to the calling code so it gives more control to the calling code
fn read_username_from_file() -> Result<String, io::Error> {
let f = File::open("hello.txt");
let mut f = match f {
Ok(file) => file,
Err(e) => return Err(e),
};
let mut s = String::new();
match f.read_to_string(&mut s) {
Ok(_) => Ok(s),
Err(e) => Err(e),
}
}
use std::fs::File;
use std::io;
use std::io::Read;
fn read_username_from_file() -> Result<String, io::Error> {
// shortcut for propagating Errors with ? operator
let mut f = File::open("hello.txt")?;
let mut s = String::new();
f.read_to_string(&mut s)?;
Ok(s)
}
fn read_username_from_file() -> Result<String, io::Error> {
let mut s = String::new();
// chaining method calls after the ? operator
File::open("hello.txt")?.read_to_string(&mut s)?;
Ok(s)
}
use std::error::Error;
use std::fs::File;
// another return type `main` can have is `Result<(), E>`
// `Box<dyn Error>` is a trait object == any kind of error
fn main() -> Result<(), Box<dyn Error>> {
let f = File::open("hello.txt")?;
Ok(())
}