rust 从关联类型实现`From` trait

pdkcd3nj  于 2023-06-30  发布在  其他
关注(0)|答案(1)|浏览(210)

我正在写一个Error enum,它表示无法解析从程序参数中获取的值。最初,它看起来像这样:

pub enum LoadingError {
    NoArg,
    CantParse
}

我希望它能很容易地从一个解析错误转换。str::parse函数定义如下:

pub fn parse<F: FromStr>(&self) -> Result<F, F::Err> {
    FromStr::from_str(self)
}

在我看来,如果我想为从任何FromStr::Err转换的enum实现From trait,我需要我的函数在FromStr trait上是泛型的。我试着这样做:

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)
    }
}

但是,这不会编译,并返回以下错误:

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;

我真的看不出这两种不同类型签名的实现会有什么冲突,而且我在Rust的核心代码中找不到impl<T> From<T> for T的定义。
我也试过使用thiserror crate,导致同样的错误。

#[derive(Error, Debug)]
pub enum LoadingError<F: FromStr> {
    #[error("can't find argument")]
    NoArg,
    #[error("couldn't parse")]
    CantParse(#[from] F::Err)
}

我的问题是:如何从FromStr trait定义的许多Err中的任何一个实现我的类型的From trait?

2w3kk1z5

2w3kk1z51#

首先,impl<T> From<T> for T在这里。
其次,任何类型都可以是FromStr::Err,包括LoadingError。如果可以创建一个只对FromStr::Err中使用的类型起作用的impl,那么就可以通过编写一个FromStr impl来从crate外部更改代码的含义,该impl使用了一个错误类型,而您认为该错误类型在FromStr::Err中没有使用。
相反,您应该在您正在寻找的实际错误类型上创建From imps,如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)
    }
}

或者,您可以创建一个方法将任何错误转换为LoadingError,并仅在parse的结果上使用它。

impl<F> LoadingError<F> {
    pub fn convert_parse<T>(result: Result<T, F>) -> Result<T, Self> {
        result.map_err(|e| LoadingError::CantParse(e))
    }
}

你可以这样使用:

LoadingError::convert_parse("1".parse::<i32>())

在错误类型上使用泛型可能会有问题,因此您可能希望存储trait对象(或使用trait对象作为泛型)。

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)))
    }
}

相关问题