Cycle in error-chain


#1

I am using error_chain in two modules that call methods on each other. Each has an error_chain link to the other.

use b;                               use a;
error_chain! {                       error_chain! {
   links {                              links {
      B(b::Error, b::ErrorKind);            A(a::Error, a:::ErrorKind)
   }                                     }
}                                     }

I am getting “recursive type ‘a::ErrorKind’ has infinite size” when compiling module a and a corresponding message when compiling module b. In other circumstances, I’d just throw the offending element into a Box, but I can’t figure out how that works inside this macro.

I really want this to work because error_chain will literally save me hundreds of lines of mostly boilerplate error handling code.


#2

The way you get around a recursive type is by adding some indirection, usually by Box-ing the errors.


#3

As I said, I have use a Box to get around this problem in my own code, but I can’t figure out the syntax to make it work with this macro.


#4

Sorry, I must have mis-read your post!

I think the problem is that the links section of the macro is designed so your errors form a kind of tree (i.e. acyclic graph), but this is introducing a cycle. I got around this by creating an error variant which contains a boxed b::Error or a::Error. I’m not sure how ergonomic it’ll turn out to be, but you should be able to do let err: a::Error = a::ErrorKind::B(box some_b_error).into() to wrap a b::Error in an a::Error.

#[macro_use]
extern crate error_chain;

mod a {
    use b;

    error_chain!{
        errors {
            B(err: Box<b::Error>)
        }
    }
}

mod b {
    use a;

    error_chain!{
        errors {
            A(err: Box<a::Error>)
        }
    }
}

fn function_which_returns_b_error(err: a::Error) -> b::Error {
    b::ErrorKind::A(Box::new(err)).into()
}

#5

Thanks. That does it, but I ran into a glitch. Module b doesn’t have any explicit error returns. It just has foo()?; bar()?;, etc. I can return a boxed error from b if I use a match on each of these function calls, but I found an easier way. My solution was to move the Error for module b into module a, which breaks the cycle. It’s a special case, but it’s simpler than any alternative I can come up with that keeps that error on the error chain.