Anyhow ¯\_(°ペ)_/¯

This library provides std::backtrace:

Anyhow ¯\_(°ペ)_/¯

This library provides anyhow::Error, a trait object based error type for easy idiomatic error handling in Rust applications.

[dependencies]
anyhow = "1.0"

Compiler support: requires rustc 1.34+


Details

  • Use Result<T, anyhow::Error>, or equivalently anyhow::Result<T>, as the return type of any fallible function.

    Within the function, use ? to easily propagate any error that implements the std::error::Error trait.

    use anyhow::Result;
    
    fn get_cluster_info() -> Result<ClusterMap> {
        let config = std::fs::read_to_string("cluster.json")?;
        let map: ClusterMap = serde_json::from_str(&config)?;
        Ok(map)
    }
    
  • Attach context to help the person troubleshooting the error understand where things went wrong. A low-level error like "No such file or directory" can be annoying to debug without more context about what higher level step the application was in the middle of.

    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))?;
        ...
    }
    
    Error: Failed to read instrs from ./path/to/instrs.json
    
    Caused by:
        No such file or directory (os error 2)
    
  • Downcasting is supported and can be by value, by shared reference, or by mutable reference as needed.

    // If the error was caused by redaction, then return a
    // tombstone instead of the content.
    match root_cause.downcast_ref::<DataStoreError>() {
        Some(DataStoreError::Censored(_)) => Ok(Poll::Ready(REDACTED_CONTENT)),
        None => Err(error),
    }
    
  • If using the nightly channel, a backtrace is captured and printed with the error if the underlying error type does not already provide its own. In order to see backtraces, they must be enabled through the environment variables described in std::backtrace:

    • If you want panics and errors to both have backtraces, set RUST_BACKTRACE=1;
    • If you want only errors to have backtraces, set RUST_LIB_BACKTRACE=1;
    • If you want only panics to have backtraces, set RUST_BACKTRACE=1 and RUST_LIB_BACKTRACE=0.

    The tracking issue for this feature is rust-lang/rust#53487.

  • Anyhow works with any error type that has an impl of std::error::Error, including ones defined in your crate. We do not bundle a derive(Error) macro but you can write the impls yourself or use a standalone macro like thiserror.

    use thiserror::Error;
    
    #[derive(Error, Debug)]
    pub enum FormatError {
        #[error("Invalid header (expected {expected:?}, got {found:?})")]
        InvalidHeader {
            expected: String,
            found: String,
        },
        #[error("Missing attribute: {0}")]
        MissingAttribute(String),
    }
    
  • One-off error messages can be constructed using the anyhow! macro, which supports string interpolation and produces an anyhow::Error.

    return Err(anyhow!("Missing attribute: {}", missing));
    

    A bail! macro is provided as a shorthand for the same early return.

    bail!("Missing attribute: {}", missing);
    

No-std support

In no_std mode, the same API is almost all available and works the same way. To depend on Anyhow in no_std mode, disable our default enabled "std" feature in Cargo.toml. A global allocator is required.

[dependencies]
anyhow = { version = "1.0", default-features = false }

Since the ?-based error conversions would normally rely on the std::error::Error trait which is only available through std, no_std mode will require an explicit .map_err(Error::msg) when working with a non-Anyhow error type inside a function that returns Anyhow's error type.


Comparison to failure

The anyhow::Error type works something like failure::Error, but unlike failure ours is built around the standard library's std::error::Error trait rather than a separate trait failure::Fail. The standard library has adopted the necessary improvements for this to be possible as part of RFC 2504.


Comparison to thiserror

Use Anyhow if you don't care what error type your functions return, you just want it to be easy. This is common in application code. Use thiserror if you are a library that wants to design your own dedicated error type(s) so that on failures the caller gets exactly the information that you choose.


License

Licensed under either of Apache License, Version 2.0 or MIT license at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this crate by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
Issues

Collection of the latest Issues

vi

vi

0

Anyhow seems to have include two backtrace strategies: one for stable and one for nightly. The former is managed by default-off Cargo feature "backtrace", but the latter is turned on automatically on nightly regardless of Cargo feature.

This may add more code to the executable, making anyhow significantly more bloated compared to Box<dyn Error+Send+Sync> in some scenarios. I see 92 additional kilobytes in .text section according to cargo bloat, which disappear when I manually edit out all Anyhow's code sections with #[cfg(backtrace)].

I think both backtracing modes should be adjustable by Cargo features. Anyhow may check #[cfg(backtrace)] only once, e.g. to compile_error! if Cargo feature for nightly-style backtraces is enabled on non-nightly. At the very least a negative Cargo feature like prohibit_backtraces should be provided to support minified builds. If user builds with -Zbuild-std-features=panic_immediate_abort to save kilobytes then backtraces are likely not welcome as well, even if natively supported by the compiler.

Related: #230.

guswynn

guswynn

0

results in

Looking briefly at the code, this appears to be at least semi-on-purpose, where the vtable for adhoc errors points to no_backtrace, but it links the backtrace back in somehow? can the ErrorImpl coercion do the same things as https://github.com/dtolnay/anyhow/blob/6833150a7965ad9f489a1da619298ba84b8d3148/src/error.rs#L880?

(also, is there a reason the backtrace is only printed for the Debug impl, not the Display impl, should the docs be updated to reflect that?)

bjorn3

bjorn3

0

-Zallow-features makes it possible to restrict which unstable features are used. Anyhow uses the backtrace feature if it detects it to be available. This check uses CARGO_ENCODED_RUSTFLAGS which contains the rustflags required for compiling for the host. When using RUSTFLAGS="-Zallow-features=" cargo build --target x86_64-unknown-linux-gnu, CARGO_ENCODED_RUSTFLAGS doesn't contain -Zallow-features=, so anyhow thinks the backtrace feature is available, however when compiling anyhow for the target -Zallow-features= is passed to rustc and thus the compilation fails.

hniksic

hniksic

5

I have a function that expects impl Error + Send + Sync + 'static:

I would like to call it with an instance of anyhow::Anyhow - for example, obtained through the anyhow! macro. I know that Anyhow doesn't implement std::error::Error, but I also know that it can be easily converted to Box<dyn Error>. However, for some reason, none of the usual tactics seem to work:

Is there a way to box Anyhow so that the resulting object actually implements Error?

vultix

vultix

2

First off, thank you for this wonderful library!

Context

It's often difficult to find where in your code an error occurred. Rust's backtraces help with this, but are often so verbose that it's still difficult.

Proposal

It would be great if you could simply glance at the error output to get file and line information. This could be implemented using #[track_caller] and can be optionally turned on using a feature flag.

I'm not sure what format you'll want, but I'm imagining something like this:

I'm willing to make a PR if you like this feature!

ndmitchell

ndmitchell

3

We've been using anyhow extensively, and it's worked out great. There's just one super confusing thing. I would expect:

To print at least My error message, and ideally on a file. But it just prints on a file. I know {:#} is available, but I've yet to find a case where we didn't want {:#} and the usage of {} is pretty pervasive - we've had it when printing messages nested, as format strings to thiserror etc. It is the bug we seem to make every single time. I appreciate compatibility constraints might be hard, but switching the two (or even making them both the same) would be easier to use.

yaahc

yaahc

1

Proposal TLDR

Add a dynamic dispatch based version of eyre::Report's EyreHandler trait and parameter, with a global hook for constructing the boxed trait object that stores additional context and handles rendering error reports.

eyre::EyreHandler::default is replaced with the hook fn, the default fn is removed from the ReportHandler trait, and the eyre::EyreHandler::display fn is renamed to report to better represent it's usage.

The proof of concept is currently implemented in https://github.com/dtolnay/anyhow/pull/97

Background

eyre is a library based on anyhow that has experimented with adding customization support to anyhow::Error to add support to forms of context in error reports that shouldn't be inserted into the chain of error messages, such as the output of tracing_error::SpanTrace.

eyre has accomplished this by adding a new ErrorHandler parameter to the primary dyn Error handling type. This approach has some downsides:

  • it breaking type inference in certain situations where anyhow! just works. [[1]]
  • exporting eyre::Report in a library interface where the ErrorHandler type is still selected by the user requires invasive use of generic parameters and breaks type inference even worse. [[2]]
  • the libraries can export different handler types in their interface requiring burdensome conversion logic
  • generic parameters likely have negative impacts on compilation times

New Design

Most of these problems can be solved by using a Boxed trait object instead of a generic parameter. Type inference is no longer an issue and behaves exactly the same as in anyhow. The hook is global for your application so any library that exports an anyhow::Error will still capture the type of context specified in the global hook, removing the need for generics in interfaces or conversions between handler types throughout the application.

The downside of this design is the addition of a Box to the ErrorImpl type behind the thin pointer, adding an extra allocation to error construction. However, in the default case this hook should create a boxed dyn object from a unit type, which wont actually allocate anything and so should be essentially free (I hope).

handler construction hook

This function is used to create the handler to be stored alongside or instead of the Backtrace. This function is called when the anyhow::Error is first constructed, letting it capture context sensitive information such as backtrace::Backtrace or a tracing_error::SpanTrace. This function takes a reference to the inner error as an argument to allow it to do conditional capture of context such as a Backtrace upon inspection of the wrapped error type.

Any trait bound

The ReportHandler trait requires that types that implement it support downcasting. This makes it possible to write extension traits for mutating the inner context. This lets store information like status codes, custom error report sections for stderr/stdout, suggestions, etc. This is necessary to implement a ReportHandler based on color-eyre.

One potential downside of this approach compared to the generic parameter is that the internals of these extension traits must rely upon downcasting that can fail, and must choose a handling strategy for when the context created by the global hook is different from the context they're written to downcast too, such as panicking or logging an warning indicating that the context couldn't be modified.

fn report

This fn provides the Debug implementation for anyhow::Error. It takes the ReportHandler as it's &self parameter letting it access and render any context stored within the handler and it takes a reference to the dyn Error stored within anyhow::Error to let it control how the chain of errors is displayed and to access other context captured by source errors intended for the error report itself, such as Backtraces.

Problems

fn backtrace(&self) -> Backtrace

Right now anyhow::Error guarantees that it will capture a std::backtrace::Backtrace when it is available. While this interface is unstable this causes some significant issues. anyhow::Error cannot delegate capturing this Backtrace to the handler because the handler could choose to not capture one, which would require an unwrap in fn backtrace to maintain the same exterior interface.

Additionally, if anyhow::Error continues to handle capturing the backtrace the hook and report printing functions cannot include the Backtrace as one of their args, because this arg would not be available on stable or even older versions once std::backtrace::Backtrace has been stabilized. This means that anyhow::Error would need to handle rendering the Backtrace it captures, preventing the handler from customizing the backtrace rendering with crates like color-backtrace. I haven't come up with a good solution for this other than making a breaking change and removing the fn backtrace(&self) -> Backtrace or changing it to return an Option and adding a second function to trait ReportHandler for accessing the inner backtrace if it exists.

danburkert

danburkert

1

Hi, I would like to be able to downcast on the just the topmost layer of the context stack. Looking through the API, it doesn't appear that this is possible. It's nearly possible by using the underlying std::error::Error provided downcasting, instead of the anyhow downcasting, but anyhow wraps the context in a private ContextError. Even if it were public, I still don't think it would be possible since I know the C context type I want, but not the E error type.

I think I can work around this in a good enough way for my usecase by comparing my_anyhow_error.downcast_ref::<MyType>().unwrap().to_string() to my_anyhow_error.to_string(), which gives me a pretty good indication that the first instance of MyType in the stack is the topmost context layer, but I'm curious if it might be a generally useful feature for anyhow.

vorner

vorner

6

Hello

I've noticed there's a (closed) issue about having a From<Box<dyn Error + Send + Sync>> impl, that refers to wanting that from std (#66)

But would it make sense to have an explicit constructor in the meantime? Eg. Error::from_box_error(..)? That way one would have to write some kind of map_error(Error::from_box_error)? instead of just ?, but that's probably better than nothing.

Versions

Find the latest versions by id

1.0.56 - Mar 07, 2022

  • Add must_use warning when an Error created by anyhow! is not used, perhaps because the programmer meant to write bail! instead (#229)

1.0.55 - Feb 21, 2022

  • Documentation improvements

1.0.54 - Feb 21, 2022

  • Construct more helpful error message from ensure! when the expression involves a negative literal const generic as the first generic argument of a method call (#224)

1.0.53 - Jan 22, 2022

1.0.52 - Dec 23, 2021

  • Reduce overhead of backtrace capture in the case that backtraces are not enabled (#212)

1.0.51 - Nov 29, 2021

  • Show doc for Ok fn

1.0.50 - Nov 28, 2021

  • Recognize more types of expressions in ensure! macro (#199, #200, #202, #203, #204, #205, #206)

1.0.49 - Nov 27, 2021

  • Add a function anyhow::Ok(v) equivalent to Ok::<_, anyhow::Error>(v) (#192)

1.0.48 - Nov 22, 2021

  • Include a Debug rendering of lhs and rhs in ensure! messages (#193, #194, #195, #196, #197, #198)

    Example:

    Before:

    After:

1.0.47 - Nov 22, 2021

  • Fixes for implicit format args support

1.0.46 - Nov 08, 2021

1.0.45 - Nov 02, 2021

  • Fix non-compilable macro expansion if downstream crate calls anyhow!, ensure!, or bail! with format args and is built with #![no_std] or #![no_implicit_prelude] (#177)

1.0.44 - Sep 12, 2021

  • Mark error constructors cold to help LLVM optimize code paths not leading to error (#166, thanks @stepancheg)

1.0.43 - Aug 14, 2021

  • Take -Zallow-features restrictions from Cargo configuration file into account on sufficiently new versions of nightly Cargo (#157, thanks @jonhoo)

1.0.42 - Jul 09, 2021

  • Enable Android's automated tooling to pull in test coverage improvements

1.0.41 - Jun 10, 2021

  • Disallow incompatible old versions of backtrace crate (#155, thanks @jfirebaugh)

1.0.40 - Mar 26, 2021

  • Reduce memory footprint of errors on Rust versions 1.51+ (#145)

1.0.39 - Mar 20, 2021

  • Add an opt-in implementation of Error::backtrace on stable compilers based on the backtrace crate (#143)

1.0.38 - Jan 11, 2021

  • Support using anyhow::Error in code executed by Miri (#134, thanks @thomcc)

1.0.37 - Dec 28, 2020

  • Improve compiler diagnostic on calling macro with a temporary value (#133)

1.0.36 - Dec 18, 2020

  • Make anyhow::Error ABI compatible with void* for round tripping through a C FFI (#132)

1.0.35 - Dec 06, 2020

  • Support 1-argument use of ensure! (#126)

1.0.34 - Nov 02, 2020

1.0.33 - Oct 06, 2020

  • Clarify documentation of anyhow!, bail!, ensure! macros (#114)

1.0.32 - Jul 24, 2020

  • Add impl From<anyhow::Error> for Box<dyn Error + Send + 'static> (#103)

1.0.31 - May 15, 2020

  • Restore "Stack backtrace" header label above the backtrace in {:?} representation (#92)

1.0.30 - May 13, 2020

  • Hide an error message that appears when building with cargo rustc -vv (#91, thanks @eoger)

1.0.29 - May 13, 2020

  • Documentation improvements

1.0.28 - Mar 30, 2020

  • Documentation improvements (#73, #78)

1.0.27 - Mar 14, 2020

  • Improve documentation of backtrace environment variable combinations (#69)

    • If you want panics and errors to both have backtraces, set RUST_BACKTRACE=1;
    • If you want only errors to have backtraces, set RUST_LIB_BACKTRACE=1;
    • If you want only panics to have backtraces, set RUST_BACKTRACE=1 and RUST_LIB_BACKTRACE=0.

Information - Updated May 20, 2022

Stars: 2.9K
Forks: 87
Issues: 11

Repositories & Extras

Rust bindings for libinjection

Add libinjection to dependencies of Cargo

Rust bindings for libinjection

CDRS is looking for maintainers

CDRS is Apache Cassandra driver written in pure Rust

CDRS is looking for maintainers

Rust bindings for the C++ api of PyTorch

LIghtweight wrapper for pytorch eg libtorch in rust

Rust bindings for the C++ api of PyTorch

Rust leveldb bindings

Almost-complete bindings for leveldb for Rust

Rust leveldb bindings

rust-analyzer is a modular compiler frontend for the Rust language

It also contains some tips &amp; tricks to help you be more productive when using rust-analyzer

rust-analyzer is a modular compiler frontend for the Rust language

Rust-Lightning is a Bitcoin Lightning library written in Rust

lightning, does not handle networking, persistence, or any other I/O

Rust-Lightning is a Bitcoin Lightning library written in Rust

Rust FUSE - Filesystem in Userspace

Rust library crate for easy implementation of Crate documentation

Rust FUSE - Filesystem in Userspace

Rust crate to implement a counterpart to the PBRT book's (3rd edition) C++ code:

Some images of the test scenes are shown below, but you can find more

Rust crate to implement a counterpart to the PBRT book's (3rd edition) C++ code:

Rust Persian Calendar

1** provides functionality for conversion among Persian (Solar Hijri) and Gregorian calendars

Rust Persian Calendar

Rust DjangoHashers

A Rust port of the password primitives used in alternatice implementation: the package library that requires OpenSSL

Rust DjangoHashers

The arkworks ecosystem consist of Rust libraries for designing and working with zero knowledge succinct...

This library is released under the MIT License and the Apache v2 License (see License)

The arkworks ecosystem consist of Rust libraries for designing and working with zero knowledge succinct...
Facebook Instagram Twitter GitHub Dribbble
Privacy