You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
For errors defined by context information and an inner source, we usually want to display both the context and the source to the user. thiserror makes this convenient through annotations like #[error("{context}: {source}")]. However, this pattern will lead to ugly and confusing duplication of information when combined with anyhow's handy feature to format source chains automatically. On the other hand, using something like #[error("{context}")] leads to worse behavior for users who invoke the error's Display impl directly rather than going through anyhow.
anyhow is very popular, but it is also reasonable to expect crucial details to be Displayable without requiring custom traversal code. How can we resolve this conflict?
One idea is for thiserror itself to provide a convenient interface for traversing source chains. Maybe this could be an inherent method, or exposed through the alternate Display mode ({:#}) as done by anyhow, giving consistent Display behavior across both crates.
I attempted implementing {:#} support by modifying the generated Display::fmt body to be
let result: std::fmt::Result = { #body };iflet e@Err(_) = result {return e;}if __formatter.alternate(){letmut next = <Selfas std::error::Error>::source(self);whileletSome(source) = next {write!(__formatter,": {}", source)?;
next = source.source();}}Ok(())
because Self isn't necessarily Error. Adding Self: Error to the where clause of the Display impl seems to cause additional problems. Maybe this could be mitigated by inlining a copy of the generated source method (if any). Thoughts?
For this to make sense though I've also had to clean up stray #[error("{context}: {source}")] for the ugly confusing duplication reason you mentioned; meaning source context is lost in the event that .unwrap() gets called on a thiserror (Which also calls Debug).
So, while at it, is it possible/reasonable to look into providing a similar Debug implementation to anyhow in addition to this alternate Display format?
Looks like there are more issues open on the topic of merge ergonomic/consistent source chain formatting (especially when considering thiserror and anyhow being complementary):
Activity
Ralith commentedon Dec 22, 2022
I attempted implementing
{:#}support by modifying the generatedDisplay::fmtbody to bebut that fails for this test case:
because
Selfisn't necessarilyError. AddingSelf: Errorto thewhereclause of theDisplayimpl seems to cause additional problems. Maybe this could be mitigated by inlining a copy of the generatedsourcemethod (if any). Thoughts?MarijnS95 commentedon Dec 23, 2022
This is the main reason why I've started returning
anyhowout offn main()instead of a customthiserrorstructure: Rust invokes theDebug(notDisplay) implementation which intentionally prints a useful source chain "backtrace": https://docs.rs/anyhow/latest/anyhow/struct.Error.html#display-representations.For this to make sense though I've also had to clean up stray
#[error("{context}: {source}")]for the ugly confusing duplication reason you mentioned; meaning source context is lost in the event that.unwrap()gets called on athiserror(Which also callsDebug).So, while at it, is it possible/reasonable to look into providing a similar
Debugimplementation toanyhowin addition to this alternateDisplayformat?https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=232a009a895d8d41d18d26e0056a55b9
MarijnS95 commentedon Dec 23, 2022
Looks like there are more issues open on the topic of merge ergonomic/consistent source chain formatting (especially when considering
thiserrorandanyhowbeing complementary):dtolnay commentedon Feb 17, 2023
Closing in favor of #98.