grahan
grahan

Reputation: 2398

How to implement From trait for custom error types?

I am currently trying to write a custom error type for my CLI application. Now I want to write an implementation of the From trait so my custom error type can wrap all third party library errors that can occur.

The error enum:

#[derive(Debug)] // Allow the use of "{:?}" format specifier
pub enum CustomError {
    Git(git2::Error),
    Other
}

Now I want to implement the From Trait for the git2::Error from the git2 library to use the ? operator in my functions.

impl From<(git2::Error)> for CustomError {
    fn from(cause: git2::Error) -> Self {
        CustomError::Git(cause)
    }
}

But when I try to use my custom error to map an error like this:

let repo = Repository::open(path).map_err(|err| CustomError::Git)?;

I am getting the following error message:

the trait `std::convert::From<fn(git2::error::Error) -> error::CustomError {error::CustomError::Git}>` is not implemented for `error::CustomError `

Can anyone help me to understand why I am getting this error and how to solve this problem ?

Any help is appreciated

Upvotes: 2

Views: 2637

Answers (1)

S&#233;bastien Renauld
S&#233;bastien Renauld

Reputation: 19662

You've mixed up a whole bunch of concepts; let's see if we can walk through this together and hopefully clarify all of it.

The git2 crate has its own error type, that you no doubt have discovered. Your definition of custom errors is fine as well.

The issue is twofold:

  1. Your implementation of From<_>

From<E> allows you to transform a type from one type to another by providing the translation function (from()).

Your implementation of this was the following:

impl From<(git2::Error)> for CustomError {
    fn from(cause: git2::Error) -> Self {
        CustomError::Git(cause)
    }
}

Brackets in rust aren't added where they should not be, and this is precisely one of the cases where this is the case. By doing this, you've actually defined From<(T)>, not From<T>. That's mistake #1.

The correct implementation simply drops the brackets:

impl From<git2::Error> for CustomError {
    fn from(cause) -> Self {
        CustomError::Git(cause)
    }
}
  1. Your actual conversion

Not an error per se, but a completely unnecessary operation as the ? operator handles it for you. There is no need for the map_err(), and if there was you'd be using into() rather than hard-calling the type (which should already be defined as a type in your function).

Remember, the whole point of conversion traits is to define them so you don't have to explicitly call them.

A final "demo" version of the code in working order could look like this:

extern crate git2;
use git2::Repository;

#[derive(Debug)] // Allow the use of "{:?}" format specifier
pub enum CustomError {
    Git(git2::Error),
    Other
}
impl From<(git2::Error)> for CustomError {
    fn from(cause: git2::Error) -> Self {
        CustomError::Git(cause)
    }
}
fn test() -> Result<(), CustomError> {
    let path = "foo";
    let output = Repository::open(path)?;
    Ok(())
}
fn main() {
    println!("Hello, world!");
}

Upvotes: 6

Related Questions