Use “anyhow” in Rust

anyhow” has gained popularity for its simplicity and flexibility in error management, and it has become a widely used library for handling errors in Rust applications”.

Anyhow” simplifies error management by allowing the use of the ? operator to propagate errors concisely.

Webdock – Fast Cloud VPS Linux Hosting

Example Code

from terminal :

cargo add anyhow
use anyhow::Result;

fn main() -> Result<()> {
    let result = divide(10, 2)?;
    println!("Result: {}", result);
    Ok(())
}

fn divide(a: i32, b: i32) -> Result<i32> {
    if b == 0 {
        anyhow::bail!("Division by zero");
    }
    Ok(a / b)
}

when you use the ? operator to propagate errors, the error “bubbles up” through the function call stack until it’s either handled with a Result type or returned from the main function.

Note : You can use the ? operator with any type that implements the std::ops::Try trait, not just “anyhow“.

Let’s take another example to illustrate how errors bubble up in Rust using the “anyhow” crate:

use anyhow::{Result, Context};

fn main() -> Result<()> {
    let result = divide_and_multiply(10, 5, 2)?;
    println!("Result: {}", result);
    Ok(())
}

fn divide_and_multiply(a: i32, b: i32, c: i32) -> Result<i32> {
    let x = divide(a, b)?;
    let y = multiply(x, c)?;
    Ok(y)
}

fn divide(a: i32, b: i32) -> Result<i32> {
    if b == 0 {
        anyhow::bail!("Division by zero");
    }
    Ok(a / b)
}

fn multiply(a: i32, b: i32) -> Result<i32> {
    if a == 0 || b == 0 {
        anyhow::bail!("Multiplication by zero");
    }
    Ok(a * b)
}

Error propagation

In this example, we have three functions: divide_and_multiply, divide, and multiply. Here’s how error propagation works:

  1. In the divide_and_multiply function, we first call divide(a, b)?. If divide encounters an error (e.g., division by zero), it will return an error that “bubbles up” to the caller. The same goes for the multiply function.
  2. When an error occurs, it is immediately returned to the calling function, and this propagation continues until the error is either handled in the main function or causes the program to exit.
  3. In the main function, we use divide_and_multiply(10, 5, 2)? to call the top-level function. If any of the lower-level functions return an error, it will be bubbled up to the main function. The ? operator automatically converts these errors into a Result, and if an error occurs, it will exit the program.

This error propagation mechanism simplifies error handling and allows you to focus on handling errors at the appropriate level in your code without excessive manual error checking and handling in each function.

anyhow::Bail! macro

Using anyhow::bail!: anyhow::bail! is a convenient macro provided by the “anyhow” crate to create and immediately return an error with a specific message. This is a straightforward way to return an error without needing to create an anyhow::Error object explicitly. For example:

fn divide(a: i32, b: i32) -> Result<i32> {
    if b == 0 {
        anyhow::bail!("Division by zero");
    }
    Ok(a / b)
}

anyhow::Error

Using anyhow::Error Directly: If you need more control over error creation or want to add additional context to the error, you can create an anyhow::Error object explicitly. This allows you to add context, chain errors, and customize the error message further. Here’s an example:

use anyhow::Result;

fn divide(a: i32, b: i32) -> Result<i32> {
    if b == 0 {
        return Err(anyhow::Error::msg("Division by zero"));
    }
    Ok(a / b)
}

Both approaches are valid and serve different purposes. The choice between them depends on the complexity of error handling and the amount of additional context you want to provide with the error. anyhow::bail! is a more concise way to return an error with a simple message, while creating an anyhow::Error object directly offers more flexibility for custom error handling and context addition.

Try the code in Rust Playground

Rust Programming

Next article

Default trait in Rust