I'm writing an Error enum
that represents failure to parse a value obtained from a program's arguments. Initially, it looks like this:
pub enum LoadingError {
NoArg,
CantParse
}
I'd like it to be easily converted from a parsing error. The str::parse
function is defined as follows:
pub fn parse<F: FromStr>(&self) -> Result<F, F::Err> {
FromStr::from_str(self)
}
It seems to me that, if I want to implement the From
trait for my enum
converting from any FromStr::Err
, I would need my function to be generic on the FromStr
trait. I tried doing something like this:
pub enum LoadingError<F: FromStr> {
NoArg,
CantParse(F::Err)
}
impl<F: FromStr> From<F::Err> for LoadingError<F> {
fn from(err: F::Err) -> LoadingError<F> {
LoadingError::CantParse(err)
}
}
This, however, does not compile, with this error being returned:
error[E0119]: conflicting implementations of trait `From<LoadingError<_>>` for type `LoadingError<_>`
--> src/main.rs:15:1
|
15 | impl<F: FromStr> From<F::Err> for LoadingError<F> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: conflicting implementation in crate `core`:
- impl<T> From<T> for T;
I don't really see how implementations with these two different type signatures would be conflicting, and I couldn't find the definition for impl<T> From<T> for T
in Rust's core code.
I've also tried using the thiserror
crate, leading me to the same error.
#[derive(Error, Debug)]
pub enum LoadingError<F: FromStr> {
#[error("can't find argument")]
NoArg,
#[error("couldn't parse")]
CantParse(#[from] F::Err)
}
My question is: How can I implement the From
trait for my type from any of the many Err
s defined by the FromStr
trait?
First, impl<T> From<T> for T
is here.
Second, any type can be FromStr::Err
, including LoadingError
. If it was possible to make an impl that only worked on types that were used in FromStr::Err
, then it would be possible to change the meaning of your code from outside your crate by writing a FromStr
impl that uses an error type that you assumed wasn't used in FromStr::Err
.
Instead, you should create From
impls on the actual error types you're looking for, like ParseIntError
.
pub enum LoadingError<F> {
NoArg,
CantParse(F)
}
use std::num::ParseIntError;
impl From<ParseIntError> for LoadingError<ParseIntError> {
fn from(value: ParseIntError) -> Self {
LoadingError::CantParse(value)
}
}
Or you can create a method to convert any error into LoadingError
and only use it on the result of parse
.
impl<F> LoadingError<F> {
pub fn convert_parse<T>(result: Result<T, F>) -> Result<T, Self> {
result.map_err(|e| LoadingError::CantParse(e))
}
}
You can use this like:
LoadingError::convert_parse("1".parse::<i32>())
Using a generic on your error type can be problematic, so you may want to store a trait object instead (or use a trait object as the generic).
use std::error::Error;
pub enum LoadingError {
NoArg,
CantParse(Box<dyn Error>)
}
use std::num::ParseIntError;
impl From<ParseIntError> for LoadingError {
fn from(value: ParseIntError) -> Self {
LoadingError::CantParse(Box::new(value))
}
}
impl LoadingError {
pub fn convert_parse<T, F: Error + 'static>(result: Result<T, F>) -> Result<T, Self> {
result.map_err(|e| LoadingError::CantParse(Box::new(e)))
}
}