3 ms·
My sibling posts a great general overview, and they make a good point about not biasing specific libraries. When I was starting out in Rust, however, I found va
by iudqnolq 6y ago
My sibling posts a great general overview, and they make a good point about not biasing specific libraries. When I was starting out in Rust, however, I found value in just doing what everyone else was doing and then deciding if I wanted to do things my own way once I was confident I could do the popular way. In that vein, here's doing error handling like a lemming.
Library: thiserror
Application: anyhow (or eyre if you want the same thing but with fancy colors and nicer backtraces).
Make sure you #[from] to derive From a lot. The value is in chains. Here's an example I just wrote
#[derive(thiserror::Error)]
enum LoadError {
#[error("Failed to parse")]
Parse(#[from] ParseError),
#[error("Failed to acquire lock")]
Lock(#[from] file_lock::AcquireError),
#[error("IO error")
Io(#[from] std::io::Error),
}
ParseError and AcquireError are defined in a similar style.
Then I have a function
fn load(...) -> Result<..., LoadError> {
let lock = FileLock::acquire(...)?;
let parsed = parse(...)?;
Ok(parsed)
}
The magic is that ? calls .into on the error, which uses a From implemention to convert the error into the error type the function returns. This means the ParseError gets converted into a LoadError::Parse, for example.
Now we've got that nice semantic cause chain, we use anyhow to display it.
fn main() -> anyhow:: Result<()> {
let index = load(...)?;
// ...
Ok(())
}
If index fails I'll get something like this printed:
Error: Failed to acquire lock
Caused by:
Failed to open lock
No such file or directory (OS error 2)
It feels like a superpower to have a high-level function fail and see the massive list of errors all the way down to the root cause.
- da39a3ee 6y agoThank you!