18 Adding a crate: anyhow
Add your first external dependency with cargo add. Use anyhow to propagate errors of (almost) any type with ?.
In the last chapter our divide() function used a String for its error type. That worked because we invented the error. But most of the time we are calling code written by someone else.
Library error types
Almost every library defines its own error types. These tend to be enums that list every kind of error that can occur. For example, parsing a string into an f64 with .parse::<f64>() returns a Result whose error type is std::num::ParseFloatError.
This is great for precision, but it becomes a chore: if one function can fail in three different ways, you’d have to juggle three different error types just to use ?.
anyhow to the rescue
Rather than naming every possible error type, we can use a dependency called anyhow. It provides anyhow::Result<T>, which lets you return (almost) any error type—thanks to some magic from Rust’s trait system that we won’t get into today.
The practical upshot: with anyhow::Result, the ? operator “just works” no matter which library’s error you’re propagating.
Adding a dependency
Up to now our Cargo.toml has had no dependencies. To add anyhow, run:
cargo add anyhowThis downloads the crate and adds it to the [dependencies] section of Cargo.toml:
[dependencies]
anyhow = "1"Using anyhow
Bring anyhow::Result into scope, then use it as the return type. Now ? can propagate the ParseFloatError from .parse() without us ever naming it:
use anyhow::Result;
impl Point {
fn from_strings(x: &str, y: &str) -> Result<Self> {
let x = x.parse::<f64>()?;
let y = y.parse::<f64>()?;
Ok(Point { x, y })
}
}Because main can also return anyhow::Result<()>, errors can bubble all the way up to the top of the program.
Exercise
- Run
cargo add anyhowto add the dependency - Define a
Pointstruct withf64fieldsxandy - Add a
from_strings(x: &str, y: &str)associated function that:- Returns
anyhow::Result<Self> - Uses
.parse::<f64>()?to parse each field
- Returns
- In
main()(returninganyhow::Result<()>), build aPointfrom two strings using? - Try passing an unparseable string (e.g.
"abc") to see the error propagate
View solution
use anyhow::Result;
#[derive(Debug)]
struct Point {
x: f64,
y: f64,
}
impl Point {
// `.parse::<f64>()` returns a `Result` whose error type belongs to the
// standard library. With `anyhow::Result`, `?` accepts (almost) any error
// type, so we never have to name it ourselves.
fn from_strings(x: &str, y: &str) -> Result<Self> {
let x = x.parse::<f64>()?;
let y = y.parse::<f64>()?;
Ok(Point { x, y })
}
}
// `anyhow::Result<()>` lets `?` bubble up any error from inside `main`.
fn main() -> Result<()> {
let point = Point::from_strings("3.0", "0.14")?;
println!("Parsed {point:?}");
// Try changing "0.14" to "abc" above: the parse fails, `?` returns the
// error, and the program prints it and exits non-zero.
Ok(())
}