Need a bit of help with error_chain


#1

I thought I’d got it, but seems not to be the case. If anyone could lend a hand, I’d appreciate it! Note that while I’m trying to produce something I want to use, I also want to learn how to do this correctly :slight_smile:

I have a binary crate, for now consisting of main.rs, lib.rs and oem.rs. Can’t really upload, so Ill reproduce the contents below. The error message I get is

error[E0277]: the trait bound `testp::errors::Error: std::convert::From` is not satisfied
  --> main.rs:28:15
   |
28 |     let tmp = OEM::from_str("L:J")?;
   |               ^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::From` is not implemented for `testp::errors::Error`
   |
   = help: the following implementations were found:
             >
             >
             >
   = note: required by `std::convert::From::from`

As noted in the comments, using chain_err on the result works, but I just want to bubble up the direct error, not produce a new one. I thought the usage of error_chain! would implement the needed From for the custom errors. Did I just use it wrong, or was my expectation wrong?

Thanks for any pointers!

Here are the files (the crate is called testp):

main.rs

extern crate testp;
use testp::errors::*;


fn main() {
    if let Err(ref e) = run() {
        eprintln!("error: {}", e);

        for e in e.iter().skip(1) {
            eprintln!("caused by: {}", e);
        }

        // The backtrace is not always generated. Try to run this example
        // with `RUST_BACKTRACE=1`.
        if let Some(backtrace) = e.backtrace() {
            eprintln!("backtrace: {:?}", backtrace);
        }

        ::std::process::exit(1);
    }
}

fn run() -> Result<()> {
    use std::str::FromStr;
    use testp::oem::OEM;

    let tmp = OEM::from_str("L:J")?;
    // this works:
    // let tmp = OEM::from_str("L:J").chain_err(|| "Clunk")?;
    Ok(())
}

lib.rs

#![recursion_limit = "1024"]

#[macro_use]
extern crate error_chain;

pub mod errors {
    error_chain!{}
}
pub mod oem;
use oem::OEM;

// I will need to factor this out
struct Loadcase {
    name: String,
    oem: OEM,
    project: String,
    category: String,
    loadcase: String,
    phase: String,
    region: String,
    engine: String,
    variant: String,
}

oem.rs

error_chain! {
    errors { 
        NoSuchOEM(s: String) {
            description("No such OEM")
            display("{}: No such OEM.",s)
        }
    }
}

use std::str::FromStr;
use std::fmt;

pub enum OEM {
    Audi,
    VW,
    Skoda,
}

impl FromStr for OEM {
    type Err = Error;

    fn from_str(s: &str) -> Result<Self> {
        use OEM::*;

        match s {
            "AU" => Ok(Audi),
            "VW" => Ok(VW),
            "SK" => Ok(Skoda),
            _ => Err(ErrorKind::NoSuchOEM(s.to_string()).into()),
        }
    }
}

impl fmt::Display for OEM {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use OEM:: *;

        let s = match *self {
            Audi => "AU",
            VW   => "VW",
            Skoda=> "SK",
        };
        write!(f, "{}", s)
    }
}

#2

What is the type of that Result there?


#3

Crikes, something ate the parameters. It’s Result<Self>, I corrected it. As far as I can tell, the signature is fixed anyways from the trait, isn’t it? Thanks :slight_smile:

(e) Weird, when trying to change it tot Result<Self, Self::Err> as in the trait I get the following error. Is there some alias for std::result::Result at work here? I have no idea how to find out, and why…

error[E0244]: wrong number of type arguments: expected 1, found 2
  --> oem.rs:22:29
   |
22 |     fn from_str(s: &str) -> Result<Self, Self::Err> {
   |                             ^^^^^^^^^^^^^^^^^^^^^^^ expected 1 type argument

#4

Yes, because error_chain typedefs type Result<T> = std::result::Result<T, Error>, where Error is the error type that error_chain generates.

Try fully qualifying the return type: -> std::result::Result<Self, Self::Err>


#5

Ok, I played around with this and I see what the problem is. You’ve defined error types in multiple modules, and there’s no From conversion for them.

You have pub mod errors { error_chain!{} } in lib.rs - that will define testp::errors::Error (and friends). In oem.rs you also have an error_chain! { errors { ... } }, which is where you define the NoSuchEOM error kind.

In your run function in main.rs, you’re returning a Result<()>, which is a typedef for testp::errors::Result<T, testp::errors::Error. OEM::from_str returns a typedef for testp::oem::Result<T, testp::oem::Error>. Note how the Error type is from different modules, and they’re not convertable - error_chain will generate the conversions (From impls) for types within the module where the error_chain! is defined, and for the std::error::Error type.

I suggest you organize the code a bit more :slight_smile:. A common idiom is to create an errors.rs file that will contain all of your crate’s error types, and they’ll be under the errors module name. If you have submodules, you can follow a similar pattern. If you need to translate between errors contained in different modules, you’ll probably need to use the foreign_links feature of error_chain.


#6

Uhhh ok, I see. I kinda expected all errors to be defined an the testp::errors module, because that’s where I importet the crate and made that mod. Seems reasonable though, and adding an error module seems like a clear way to do it anyways, so I’m gonna try that tomorrow.

Thanks a lot!


#7

You did define some error types in testp::errors (by virtue of that empty error_chain!{} block), but you also have the error_chain! block in oem.rs, which will define that stuff in that module.


#8

Yes, I expected that call to add to the already existing module. Seems pretty unreasonable in hindsight, though :slight_smile: