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:
- In the
divide_and_multiply
function, we first calldivide(a, b)?
. Ifdivide
encounters an error (e.g., division by zero), it will return an error that “bubbles up” to the caller. The same goes for themultiply
function. - 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. - In the
main
function, we usedivide_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 themain
function. The?
operator automatically converts these errors into aResult
, 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