Displaying the error

Since we want to print the error to the terminal, we'll implement the Display trait for our Error type:

use std::fmt::{self, Display, Formatter};

use self::Error::*;

impl Display for Error {
    fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
        match *self {
            FromUtf8(ref error) => error.fmt(formatter),
            Io(ref error) => error.fmt(formatter),
            Utf8(ref error) => error.fmt(formatter),
            Msg(ref msg) => write!(formatter, "{}", msg),
        }
    }
}

For the three cases where we wrap an error from another type, we just call the corresponding fmt() method of these errors. In the case that it is a Msg, we write the string using the write! macro. This macro is a bit similar to print!, but needs a parameter to specify where to write the formatted data.

It is not very helpful in our case, but it is recommended to also implement the Error trait for custom error types:

use std::error;

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            FromUtf8(ref error) => error.description(),
            Io(ref error) => error.description(),
            Utf8(ref error) => error.description(),
            Msg(ref msg) => msg,
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        let cause: &error::Error =
            match *self {
                FromUtf8(ref error) => error,
                Io(ref error) => error,
                Utf8(ref error) => error,
                Msg(_) => return None,
            };
        Some(cause)
    }
}

The only required method of this trait is description(), which returns a short description of the error. Again, in the three cases, we just call the description() method from the wrapped type itself. And, for our Msg variant, we return the wrapped message.

It is possible that we don't have a string to return from this method. If it is the case, we can just return &'static str, like this:

Io(_) => "IO error",

The cause() method is optional and is used to return the cause of the error. Here, we return the inner error when there's one in the variant and return None for our Msg variant.

The trait Error requires the Self type to implement both Display and Debug. We implemented Display earlier, but we don't implement Debug yet. Let's fix that by adding an attribute in front of the type declaration:

#[derive(Debug)]
pub enum Error {
    FromUtf8(FromUtf8Error),
    Io(io::Error),
    Msg(String),
    Utf8(Utf8Error),
}

It is good practice to provide a type alias named Result that is specialized for our error type. Let's write one:

use std::result;

pub type Result<T> = result::Result<T, Error>;

By doing so, we hide the original Result type from the standard library. That's why we're specifying a qualified version of this type. Otherwise, the compiler will assume that it is a recursive type, which is not the case here. We'll have to be careful when we import this type in other modules, because it hides the Result type. In case we want to use the original Result type, we'll have to use the same trick; qualifying it.