snafu icon indicating copy to clipboard operation
snafu copied to clipboard

alternate `Display` to include the source chain

Open BugenZhao opened this issue 2 years ago • 8 comments

Hi, and thanks for your work on this great library!

I'm considering if it's possible to support displaying the source chain with alternate Display (i.e. {:#}), which seems to be a convention for error reporting utilized in many other error handling libraries, including anyhow and error-stack.

  • https://docs.rs/anyhow/1.0.72/src/anyhow/fmt.rs.html#10-14
  • https://docs.rs/error-stack/0.3.1/src/error_stack/fmt.rs.html#1014-1019

BugenZhao avatar Aug 04 '23 07:08 BugenZhao

Does report or Report not work for you?

shepmaster avatar Aug 04 '23 11:08 shepmaster

Yeah, it seems Report has a similar idea. However, I have several concerns...

  • I'm not sure if developers always remember to wrap the Error into Report before printing them out, or they'll lose the information from the sources if {source} is not included in #[snafu(display(..))] (which I guess should be a good practice?).
  • It looks like Report always includes the backtrace if provided, which might not be suitable to appear in a user-facing error message. As a result, we have to introduce our own Display wrapper.

BugenZhao avatar Aug 07 '23 05:08 BugenZhao

I'm not sure if developers always remember

How would this be different from remembering to use the alternate display flag?

which I guess should be a good practice

The current best practice is to do exactly one of:

  • include the source in the Display implementation
  • link to the source via Error::source

Report always includes the backtrace if provided

Right now, it only happens on nightly Rust if you've enabled the unstable-provider-api feature flag, but at some point in the future it might happen by default. We might also add some small configuration knobs.

shepmaster avatar Aug 07 '23 12:08 shepmaster

utilized in many other error handling libraries

I believe the big difference is that these other libraries (and I'm not an expert on them) don't actually give you control over the implementation of Display in the first place, while SNAFU does. For example, could you share your desired syntax to specify the regular and alternate Display implementations for a SNAFU-enhanced error?

We have had some discussions about some kind of #[snafu(display(false))] style option that would disable the creation of the Display implementation, letting the user define it completely by hand. That would allow uncommon / highly special cases without completely jettisoning SNAFU.

shepmaster avatar Aug 07 '23 12:08 shepmaster

We have had some discussions about some kind of #[snafu(display(false))] style option that would disable the creation of the Display implementation, letting the user define it completely by hand. That would allow uncommon / highly special cases without completely jettisoning SNAFU.

Linking #473, as more people have suggested this feature.

Enet4 avatar Jun 21 '25 16:06 Enet4

I'm running into this now as well, after realizing that the reason we didn't get good error logs in iroh was because we weren't wrapping our errors in our custom error type. We did however not forget to use the alternate display. I think it's fairly intuitive to expect the alternate display to be a more elaborate, human-readable error print.

What's the danger of implementing alternate display to show sources? Currently the normal and alternate display do the same thing, and people expect the alternate display to show sources intuitively.

matheus23 avatar Aug 29 '25 08:08 matheus23

I suppose the main clash comes from SNAFU having a dedicated type for error reporting, rather than choosing to combine both handling and reporting into the constructed error types. While admittedly both concerns (and others such as adding context) are important, note that there isn't exactly a standard or convention on how to do this: anyhow offers an alternate display implementation via {:#}, but errors created via thiserror do not. eyre also supports {:#}, but it also takes one step further on this by including the EyreHandler trait to let consumers build their own display formats. I suppose people only expect it because they are specifically using a lot of anyhow and/or eyre.

The two non-exclusive ways I can see right now that would probably help are:

  • a) With SNAFU, consumers have an opinionated Report wrapper for error reporting. If they don't like any of the forms provided by Report, they need to write their own wrapper. We could probably offer some construct to help build custom report wrappers that work as drop-in replacements.
  • b) Add the alternate() clause to all Display impls for generated errors, with the documented notice that wrapping the error with Report is preferred. The problem I see here is that we'd be generating more code for a use case that is not exactly recommended.

If there is a proof-of-concept that shows some form of error display which cannot be done using any constructs from SNAFU, that could be an assessment worth making.

Enet4 avatar Aug 29 '25 09:08 Enet4

a) With SNAFU, consumers have an opinionated Report wrapper for error reporting. If they don't like any of the forms provided by Report, they need to write their own wrapper. We could probably offer some construct to help build custom report wrappers that work as drop-in replacements.

We have our own wrapper in n0_snafu.

This is the code in question where we're logging a #[derive(Snafu)] error:

tracing::warn!("probe failed: {:#}", err);
Err(probes_error::ProbeFailureSnafu {}.into_error(err))

We can implement an alternate display for our own wrapper, but then this code wouldn't compile:

tracing::warn!("probe failed: {:#}", n0_snafu::Error::from(err));
Err(probes_error::ProbeFailureSnafu {}.into_error(err))

Because n0_snafu::Error owns err.

Now, there are workarounds:

  • Make your error clonable (this cannot always be done)
  • Add a lifetime to n0_snafu::Error (this seems very untypical and unergonomic)
  • Add a separate wrapper that implements our desired Display logic

We'll always be able to fix our issue by doing the last thing, but it seems wasteful and error prone, given snafu errors already implement Display.

matheus23 avatar Aug 29 '25 12:08 matheus23