This is the documentation for anyhow's Context:
/// Wrap the error value with additional context.
fn context<C>(self, context: C) -> Result<T, Error>
where
C: Display + Send + Sync + 'static;
/// Wrap the error value with additional context that is evaluated lazily
/// only once an error does occur.
fn with_context<C, F>(self, f: F) -> Result<T, Error>
where
C: Display + Send + Sync + 'static,
F: FnOnce() -> C;
In practice, the difference is that with_context
requires a closure, as shown in anyhow's README:
use anyhow::{Context, Result};
fn main() -> Result<()> {
// ...
it.detach().context("Failed to detach the important thing")?;
let content = std::fs::read(path)
.with_context(|| format!("Failed to read instrs from {}", path))?;
// ...
}
But it looks like I can replace the with_context
method with context
, get rid of the closure by deleting ||
, and the behaviour of the program wouldn't change.
What is the difference between the two methods under the hood?
The closure provided to with_context
is evaluated lazily, and the reason you'd use with_context
over context
is the same reason you'd choose to lazily evaluate anything: it rarely happens and it's expensive to compute. Once those conditions are satisfied then with_context
becomes preferable over context
. Commented pseudo-example:
fn calculate_expensive_context() -> Result<()> {
// really expensive
std::thread::sleep(std::time::Duration::from_secs(1));
todo!()
}
// eagerly evaluated expensive context
// this function ALWAYS takes 1+ seconds to execute
// consistently terrible performance
fn failable_operation_eager_context(some_struct: Struct) -> Result<()> {
some_struct
.some_failable_action()
.context(calculate_expensive_context())
}
// lazily evaluated expensive context
// function returns instantly, only takes 1+ seconds on failure
// great performance for average case, only terrible performance on error cases
fn failable_operation_lazy_context(some_struct: Struct) -> Result<()> {
some_struct
.some_failable_action()
.with_context(|| calculate_expensive_context())
}
As the documentation for anyhow::Context::with_context states:
Wrap the error value with additional context that is evaluated lazily only once an error does occur.
If what is passed to context
might be computationally expensive, it is better to use with_context
, as the closure passed is evaluated only when with_context
is called. This is referred to as being evaluated in a lazy and not eager manner.
Similar behavior exists in the standard library, e.g:
Eager | Lazy |
---|---|
Option::or | Option::or_else |
Option::unwrap_or | Option::unwrap_or_else |
Option::map_or | Option::map_or_else |
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With