The last thing we need to do in order to use all the good practices for error types in Rust is to make them easy to compose, because, for now, if we have another error type, such as io::Error, we would need to use the following code every time we have another type:
let val = match result { Ok(val) => val, Err(error) => return Err(Error::Io(error)), };
This can quickly become cumbersome. To improve that, we'll implement the From trait for different error types:
impl From<io::Error> for Error { fn from(error: io::Error) -> Self { Io(error) } } impl<'a> From<&'a str> for Error { fn from(message: &'a str) -> Self { Msg(message.to_string()) } } impl From<Utf8Error> for Error { fn from(error: Utf8Error) -> Self { Utf8(error) } } impl From<FromUtf8Error> for Error { fn from(error: FromUtf8Error) -> Self { FromUtf8(error) } }
These implementations are easy to understand: if we have an io::Error, we just wrap them in the corresponding variant. We also added a convenient conversion from the &str type.
This will allow us to use the following, which is not really better, but the good old ? operator will help us to reduce the boilerplate:
let val = match result { Ok(val) => val, Err(error) => return Err(error.into()), };