Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Retrieve full error message from type-erased anyhow::Error #123

Closed
hniksic opened this issue Nov 11, 2020 · 4 comments
Closed

Retrieve full error message from type-erased anyhow::Error #123

hniksic opened this issue Nov 11, 2020 · 4 comments

Comments

@hniksic
Copy link

hniksic commented Nov 11, 2020

I am dealing with an error enum that has a catch-all variant that uses Box<dyn Error + Send + Sync + 'static>. This works with anyhow::Error just fine (with into() or ?), but there is a problem when reporting the error. When showing the error message, the code uses Display or to_string() on the Box<dyn Error>. With e.g. an IO error this is OK, but with anyhow::Error it prints just the outermost context. In that particular place I need it to print the full list of causes as well as the root error.

I tried the following:

  • call Error::downcast_ref() to downcast Box<dyn Error + ...> to anyhow::Error - doesn't compile because anyhow::Error doesn't implement std::error::Error.
  • specify the alternate format, format!("{:#}", err) - doesn't appear to make a difference in the output.
  • use the debug output, format!("{:?}", err) - works, but only for anyhow::Error - for all other errors, it prints the low-level output meant for debugging, and I can't detect whether the box holds an anyhow::Error.
  • construct anyhow::Error from the box, and then use the debug output - doesn't compile because the constructor consumes the box, and I only have a reference to the box. (I need to generate the message from Display::fmt and Debug::fmt on my error, where I get &self.)

I could easily fix the problem if I could do any of the following:

  • use one of the functions provided by the Error trait to obtain the full error message.
  • downcast &Box<dyn Error + ...> to Option<&anyhow::Error> (returning Some if anyhow::Error is inside).
  • given a Box<dyn Error + ...>, detect whether an anyhow::Error is inside;

Is that possible, or is there some other approach that I haven't considered to print the full error information?

@hniksic hniksic closed this as completed Nov 11, 2020
@hniksic hniksic reopened this Nov 11, 2020
@dtolnay
Copy link
Owner

dtolnay commented Nov 11, 2020

The easiest would be to store your catch-all as anyhow::Error rather than Box<dyn Error + Send + Sync + 'static>.

Alternatively you can iterate causes of an arbitrary error like this:

use anyhow::anyhow;
use std::error::Error;

fn main() {
    let err = anyhow!("one").context("two").context("three");
    print(&err.into());
}

fn print(err: &Box<dyn Error + Send + Sync + 'static>) {
    for e in anyhow::Chain::new(err.as_ref()) {
        println!("{}", e);
    }
}

@hniksic
Copy link
Author

hniksic commented Nov 11, 2020

The easiest would be to store your catch-all as anyhow::Error rather than Box<dyn Error + Send + Sync + 'static>.

Indeed, I have that implemented on the development branch, and it of course works perfectly. The consequence it that it bakes anyhow::Error into the error type, which some see as a downside.

Alternatively you can iterate causes of an arbitrary error like this:

Thanks, I'll use this if we decide to keep Box<dyn Error>. It does requires reimplementing the "caused by" and the enumeration pretty-printing, but that's not a big deal.

@hniksic
Copy link
Author

hniksic commented Nov 12, 2020

It does requires reimplementing the "caused by" and the enumeration pretty-printing

To expand on this, I see value in being able to obtain the whole message exactly as anyhow would format it, given a Box<dyn Error>. (There is also #119 which requests an explicit API for such formatting on anyhow::Error itself.) It provides interoperability with code that aims to support the use of anyhow in callbacks and such, but still uses Box<dyn Error> in variants of its error enum. To avoid namespace pollution, the formatting function could perhaps be an associated function on anyhow::Chain.

If you don't think such a function is desirable, feel free to close this issue, since the problem I was having is resolved with your last comment.

Repository owner deleted a comment from let4be Feb 21, 2022
@dtolnay
Copy link
Owner

dtolnay commented Feb 21, 2022

I'll go ahead and close this. #123 (comment) are still the 2 recommended approaches.

@dtolnay dtolnay closed this as completed Feb 21, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants