Repository: rust-lang-deprecated/failure (language: rust)

Issue:
How to chain failure::Error ?
I have multiple modules with functions returning failure::Error and I want to chain the returned Errors. It is very simple to assign a std::error::Error to the `#[cause]`, but it's not possible with failure::Error.

To workaround this I used the available failure::Compat and compat(), but then I run into the issue how to print the complete error chain.

This code demonstrates my problem

~~~
#[macro_use]
extern crate failure;
use failure::{Compat, Error, Fail};

mod a {
    use std;
    use failure::{Compat, Error};
    use b;

    #[derive(Fail, Debug)]
    pub enum MyError {
        // #[fail(display = "module a failed")] Failed(#[cause] Error,
        #[fail(display = "module a failed")] Failed(#[cause] Compat<Error>),
    }

    pub fn fails() -> std::result::Result<(), Error> {
        b::fails().map_err(|err| MyError::Failed(err.compat()))?;
        Ok(())
    }
}

mod b {
    use std;
    use failure::Error;
    use std::fs::File;

    #[derive(Fail, Debug)]
    pub enum MyError {
        #[fail(display = "module b failed")] Failed(#[cause] std::io::Error),
    }

    pub fn fails() -> std::result::Result<(), Error> {
        File::open("foo.txt").map_err(|err| MyError::Failed(err))?;
        Ok(())
    }
}

pub fn print_compat_err(err: Error) {
    let mut fail: &Fail = err.cause();
    println!("{}", fail);

    while let Some(cause) = fail.cause() {
        println!("caused by: {}", cause);
        if let Some(compat) = cause.downcast_ref::<Compat<Error>>() {
            fail = compat.inner_ref().cause();
        } else {
            fail = cause;
        }
    }
}

fn print_err(err: Error) {
    let mut fail: &Fail = err.cause();
    println!("{}", fail);

    while let Some(cause) = fail.cause() {
        println!("caused by: {}", cause);
        fail = cause;
    }
}

fn main() {
    if let Err(err) = b::fails() {
        print_err(err);
    }

    if let Err(err) = a::fails() {
        print_err(err);
    }

    if let Err(err) = a::fails() {
        print_compat_err(err);
    }
}
~~~

When printing the error chain with "print_err(err);" then printout for Error returned by "a::fails()" is truncated.

~~~
module a failed
caused by: module b failed
~~~

Printing the error chain with "print_compat_err(err);" displays the expected result.

~~~
module a failed
caused by: module b failed
caused by: No such file or directory (os error 2)
~~~

compat.rs only offers wrapper "into_inner(self)", but for this I need to 'get a reference to the inner error of Compat

~~~
   /// get ref to inner error.
   pub fn inner_ref(&self) -> &E {
       &self.error
   }
~~~

Can this fn be added to the crate?

Or is my use case to return failure::Error in each module not a proper approach.

Any advice and help are welcome.


Discussion / hints:
This is an interesting problem.

The correct code to *generate* would be to call `.cause()` on the error (or, in #168, `as_fail`). I don't see an easy way to automatically do that, we might need a second attribute for when you want to store the cause as a `failure::Error`.
I was also trying to annotate `#[cause]` for a failure::Error in a variant of a custom `#[derive(Fail)]` enum, and ran into the same original problem described by @rainboh, with compile error given below.  Instead of using `Compat<failure::Error>`, I went ahead and implemented `Display` and `Fail` manually, using `failure::Error::cause()` for my impl. of `Fail::cause` with this variant.   @withoutboats: Is that not the best workaround for the original problem, lacking a means to derive and generate the same?  #168 also suggests "someday when Error can implement Fail" which would presumably also solve this problem?

~~~
error[E0277]: the trait bound `failure::Error: std::error::Error` is not satisfied
  --> src/barc.rs:65:10
   |
65 | #[derive(Fail, Debug)]
   |          ^^^^ the trait `std::error::Error` is not implemented for `failure::Error`
   |
   = note: required because of the requirements on the impl of `failure::Fail` for `failure::Error`
   = note: required for the cast to the object type `failure::Fail`
~~~
@mitsuhiko I was just trying, per above, to get clarification on a best workaround recommendation.  Reasonable request? I don't expect an actual fix was included in 0.1.2, but workaround guidance would be valuable here. 
@dekellum i want to look at this after 0.1.2 is out. I'm generally really unhappy with `Error` but i also don't have a good idea yet how to fix it.
Again, just to be clear, I was (originally with @withoutboats above) just requesting workaround guidance for this issue with `derive(Fail)`. But maybe your response, regarding `Error`, is more directly related than I am currently understanding? Thanks again.
I assume this issue here is effectively a duplicate of #187?
I think that's a fair assumption, but just to be clear, I'm looking for a recommended workaround for this variant (or symptom) of the core issue. :=)
Also #187 is but one potential fix as a PR.
As mentioned there I do not know yet how to deal with this and I do not have a good plan yet. Also obviously no workaround.
I proposed an alternative workaround [above](#issuecomment-378352347), to manually `impl Fail`.  Given the radio silence for 3 months, I'm concerned there might be something wrong with it, and so I'm looking for some comment as to its applicability.  
Also, @mitsuhiko, you say "obviously no workaround". This issue proposes two possible workarounds: one using `Compat`, one with manual `impl Fail`. Here we are talking about workarounds for the *symptom* (noted compile error), not the underlying design *cause*, that we expect will require a breaking change. Both workarounds might be situationally valid? Would it not help users of the new 0.1.2 or earlier releases to see docs or a FAQ entry (possibly just this issue remaining open) with some validation of either or both of those workarounds, or Pros/Cons of each?  Regarding potential pros of the latter workound: its more future-proof in that when `impl Fail for Error` becomes possible, and is done, there is no redundant `Compat` wrapper. Thoughts, or am I still missing the larger issue?
> Would it not help users of the new 0.1.2 or earlier releases to see docs or a FAQ entry (possibly just this issue remaining open) with some validation of either or both of those workarounds

I don't think this is the most pressing issue with failure right now and I do not actually want to encourage workarounds for now. IMO users should not be encouraged *for now* to embed errors in errors. I effectively want to postpone dealing with this issue until I managed to wrap my head around it.
"Not encourag[-ing]" is effectively not encouraging use of the *failure* crate, without replacement. But thanks for reading, I feel you've heard me out, and your new work on *failure* is greatly appreciated! FWIW, I think that train has left the station and pretending otherwise is less than helpful, but leaving this issue open would be a reasonable compromise from my perspective. :-)



Fix patch:
diff --git a/examples/error_as_cause.rs b/examples/error_as_cause.rs
new file mode 100644
index 0000000..24e5b06
--- /dev/null
+++ b/examples/error_as_cause.rs
@@ -0,0 +1,18 @@
+#[macro_use]
+extern crate failure;
+
+use failure::{err_msg, Error, Fail};
+
+#[derive(Debug, Fail)]
+#[fail(display = "my wrapping error")]
+struct WrappingError(#[fail(cause)] Error);
+
+fn bad_function() -> Result<(), WrappingError> {
+    Err(WrappingError(err_msg("this went bad")))
+}
+
+fn main() {
+    for cause in Fail::iter_causes(&bad_function().unwrap_err()) {
+        println!("{}", cause);
+    }
+}
diff --git a/failure_derive/src/lib.rs b/failure_derive/src/lib.rs
index 230d2ee..4e9b22f 100644
--- a/failure_derive/src/lib.rs
+++ b/failure_derive/src/lib.rs
@@ -19,7 +19,7 @@ fn fail_derive(s: synstructure::Structure) -> TokenStream {
 
     let cause_body = s.each_variant(|v| {
         if let Some(cause) = v.bindings().iter().find(is_cause) {
-            quote!(return Some(#cause))
+            quote!(return Some(::failure::AsFail::as_fail(#cause)))
         } else {
             quote!(return None)
         }
diff --git a/src/as_fail.rs b/src/as_fail.rs
new file mode 100644
index 0000000..daa7e2d
--- /dev/null
+++ b/src/as_fail.rs
@@ -0,0 +1,37 @@
+use Fail;
+
+/// The `AsFail` trait
+///
+/// This trait is similar to `AsRef<Fail>`, but it is specialized to handle
+/// the dynamic object of `Fail`. Implementors of `Fail` have a blanket
+/// implementation. It is used in `failure_derive` in order to generate a
+/// custom cause.
+pub trait AsFail {
+    /// Converts a referece to Self into a dynamic trait object of `Fail`.
+    fn as_fail(&self) -> &Fail;
+}
+
+impl<T> AsFail for T
+where
+    T: Fail,
+{
+    fn as_fail(&self) -> &Fail {
+        self
+    }
+}
+
+impl AsFail for Fail {
+    fn as_fail(&self) -> &Fail {
+        self
+    }
+}
+
+with_std! {
+    use error::Error;
+
+    impl AsFail for Error {
+        fn as_fail(&self) -> &Fail {
+            self.as_fail()
+        }
+    }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 1002e76..cce5008 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -39,6 +39,7 @@ mod box_std;
 mod compat;
 mod context;
 mod result_ext;
+mod as_fail;
 
 use core::any::TypeId;
 use core::fmt::{Debug, Display};
@@ -47,6 +48,7 @@ pub use backtrace::Backtrace;
 pub use compat::Compat;
 pub use context::Context;
 pub use result_ext::ResultExt;
+pub use as_fail::AsFail;
 
 #[cfg(feature = "failure_derive")]
 #[allow(unused_imports)]


Test patch: