Application-level Rust errors often need more context than the raw error returned by std or another crate. The anyhow crate fits binaries and command-line tools where the user needs a readable failure report instead of a public typed error API.

anyhow::Result gives fallible functions one return type while the ? operator still propagates I/O, parsing, and library errors. The Context trait adds the operation that failed, so a missing file report can name the path instead of stopping at a generic OS error.

Use anyhow at the application boundary, such as main, task runners, and internal CLI helpers. Library crates that expect downstream callers to match individual error variants usually keep a typed error enum and reserve anyhow for binaries, tests, or examples.

Steps to handle Rust errors with anyhow:

  1. Open a terminal in the Rust binary package root.

    The directory should contain the Cargo.toml file for the application that will report contextual errors.

  2. Add anyhow to the application dependencies.
    $ cargo add anyhow
        Updating crates.io index
          Adding anyhow v1.0.102 to dependencies
                 Features:
                 + std
                 - backtrace
        Updating crates.io index
         Locking 1 package to latest Rust 1.96.0 compatible version

    cargo add anyhow records the dependency in Cargo.toml and updates Cargo.lock for an application package.

  3. Replace the binary entry point with an anyhow result and context.
    src/main.rs
    use anyhow::{Context, Result};
    use std::{env, fs, path::PathBuf};
     
    fn main() -> Result<()> {
        let path = env::args_os()
            .nth(1)
            .map(PathBuf::from)
            .unwrap_or_else(|| PathBuf::from("Cargo.toml"));
     
        let contents = fs::read_to_string(&path)
            .with_context(|| format!("failed to read {}", path.display()))?;
     
        println!("loaded {} bytes from {}", contents.len(), path.display());
        Ok(())
    }

    with_context() delays the formatted message until the read fails. Use context() when the added message does not need values from the current scope.

  4. Run the binary with a file that exists.
    $ cargo run -- Cargo.toml
     Downloading crates ...
      Downloaded anyhow v1.0.102
       Compiling anyhow v1.0.102
       Compiling demo-cli v0.1.0 (/work/demo-cli)
        Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.68s
         Running `target/debug/demo-cli Cargo.toml`
    loaded 98 bytes from Cargo.toml

    The successful path returns Ok(()) from main after printing the byte count.

  5. Run the binary with a missing file.
    $ cargo run -- missing.toml
        Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.00s
         Running `target/debug/demo-cli missing.toml`
    Error: failed to read missing.toml
    
    Caused by:
        No such file or directory (os error 2)

    The first line comes from with_context(). The Caused by section keeps the original I/O error that ? propagated.