Things on this page are fragmentary and immature notes/thoughts of the author. Please read with your own judgement!
:timing
:sccache 1
Tips and Traps¶
The question mark
?
is a sugar syntax ...eprintln!
Result.expect
is a more user friendly alternative toResult.unwrap
.Result.expect
allows you to define a customized error message. It is suggested that you useResult.expect
instead ofResult.unwrap
where applicable. Of course, bothResult.expect
andResult.unwrap
are discouraged and it is suggested that you use pattern matching and handle errors explicitly.Result.ok
converts aResult
object to anOption
object. Symetrically,Option.ok_or
converts anOption
to aResult
object.The recommended way of error handling in Rust is to define an enum that covers meaningful error cases for your library, implement
Debug
,Display
andstd::error::Error
for that enum. This is fast/cheap (no heap allocation on error), and precise and easy to use.By default errors in Rust are checked (at compile time). However, you can get unchecked error using
Box<dyn std::error::Error + 'static>
withSend
andSync
.The disscussion in The state of error handling in the 2018 edition suggests that thiserror (for libraries) + anyhow (for applications) is a good combination.
Rust Crates for Error Handling¶
thiserror¶
anyhow¶
miette¶
miette is a fancy diagnostic reporting library and protocol for us mere mortals who aren't compiler hackers.
:dep thiserror = "1.0.25"
:dep anyhow = "1.0.41"
use thiserror::Error;
#[derive(Error, Debug)]
pub enum ParseRankError {
#[error("{0} is not a valid symbol for card rank!")]
InvalidSymbol(char),
#[error("{0} is not a valid integer for card rank!")]
InvalidInteger(u8),
}
let err = ParseRankError::InvalidSymbol('m');
err
err.to_string()
println!("{}", err);
println!("{:?}", err);
println!("{:?}: {}", err, err);
eprintln!("{:?}: {}", err, err);
use std::io;
let err = io::Error::new(io::ErrorKind::Other, "oh no!");
err
eprintln!("{:?}: {}", err, err);
println!("{:?}", io::Error::last_os_error());
use thiserror::Error;
/// WordCountError enumerates all possible errors returned by this library.
#[derive(Error, Debug)]
pub enum WordCountError {
/// Represents an empty source. For example, an empty text file being given
/// as input to `count_words()`.
#[error("Source contains no data")]
EmptySource,
/// Represents a failure to read from input.
#[error("Read error")]
ReadError { source: std::io::Error },
/// Represents all other cases of `std::io::Error`.
#[error(transparent)]
IOError(#[from] std::io::Error),
}
/// WordCountError enumerates all possible errors returned by this library.
#[derive(Debug)]
enum WordCountError {
/// Represents an empty source. For example, an empty text file being given
/// as input to `count_words()`.
EmptySource,
/// Represents a failure to read from input.
ReadError { source: std::io::Error },
/// Represents all other cases of `std::io::Error`.
IOError(std::io::Error),
}
impl std::error::Error for WordCountError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match *self {
WordCountError::EmptySource => None,
WordCountError::ReadError { ref source } => Some(source),
WordCountError::IOError(_) => None,
}
}
}
impl std::fmt::Display for WordCountError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match *self {
WordCountError::EmptySource => {
write!(f, "Source contains no data")
}
WordCountError::ReadError { .. } => {
write!(f, "Read error")
}
WordCountError::IOError(ref err) => {
err.fmt(f)
}
}
}
}
impl From<std::io::Error> for WordCountError {
fn from(err: std::io::Error) -> WordCountError {
WordCountError::IOError(err)
}
}