70 %
Chris Biscardi

anyhow

Why would we use anyhow::Result instead of Rust's built-in Result type?

In Rust there are two types of errors: Recoverable and Unrecoverable. We'll ignore unrecoverable errors because anyhow is a library for working with recoverable errors.

Unrecoverable errors are panics.

anyhow::Result is a core::result::Result specialized to anyhow's anyhow::Error struct. The definition in anyhow is a type alias.

rust
pub type Result<T, E = Error> = core::result::Result<T, E>;

This means anyhow::Result is the core::result::Result you may be used to, with the error type already set. The following two types are equivalent

rust
fn main() -> Result<(), anyhow::Error> {}
rust
fn main() -> anyhow::Result<()> {}

This works out really well for us because anyhow::Error is a fairly transparent layer over any error type that allows adding additional context to errors that get propagated back alongside the original error.

This allows us to return both third party errors, such as those from the std library or another crate and own own errors.

In the validation function, we can construct our own anyhow::Error with the anyhow! macro

rust
anyhow!("missing X-Signature-Ed25519 header")

as well as return errors from third party crates with additional application-specific context. For example, This isn't just a hex::decode error, it's an error decoding a specific header.

rust
let decoded_header = hex::decode(header_signature)
.context(
"Failed to decode ed25519 signature header",
)?;