我正在编写一个新的crate,希望它可以与另一个crate中定义的任何trait实现一起使用。该trait大致如下:
pub trait Trait {
type Error;
...
}
我有自己的Error
类型,但有时我只想原样转发底层错误。我的直觉是定义一个如下的类型:
pub enum Error<T: Trait> {
TraitError(T::Error),
...
}
这类似于 thiserror 推荐的模式,看起来很通用。它可以正常工作,但我也想在我的实现中使用
?
,所以我需要实现 From
:impl<T: Trait> From<T::Error> for Error<T> {
fn from(e: T::Error) -> Self { Self::TraitError(e) }
}
那样做会失败,因为它与
impl<T> core::convert::From<T> for T
相冲突。我想我明白原因了——其他实现Trait
的人可能会设置type Error = my_crate::Error
,以使两个impl
都适用——但我该如何实现类似的语义呢?我查看了一些其他的crate,它们似乎通过使它们的
Error
(或等效物)针对错误类型本身而不是trait实现来处理这个问题。当然,这可以解决问题,但是:
- 在我们拥有固有关联类型之前,这会更加冗长。我的
T
实际上实现了多个trait,每个trait都有自己的Error
类型,因此现在我必须返回像Result<..., Error<<T as TraitA>::Error, <T as TraitB>::Error>>
这样的类型; - 这可能表达能力较差(因为与
Trait
的关系丢失了)。
如今,对于我的Error
而言,是否将其泛化为各种类型是最佳(最符合惯用法)的选项?
From
,你可以使用.map_err(Error::TraitError)?
代替简单的?
,这样错误类型就是Error
而不是T::Error
,并且?
将使用通用的impl<T> From<T> for T
,其中T = Error
。 - Filipe RodriguesFrom
特质只对类型进行操作,它不知道它来自哪里。如果TraitA::Error
和TraitB::Error
有相同的类型会发生什么?我仍然需要使Error
成为泛型,但它可以针对一个类型进行泛型化,而不是每个单独的特质。 - jbramleyT
与T: TraitA + TraitB
绑定,并在Error
中有两个变体,一个是TraitA(<T as TraitA>::Error)
,另一个是TraitB(<T as TraitB>::Error)
,并根据使用的特征使用.map_err(...)?
吗?即使它们具有相同的类型,它们也会处于不同的变体中,如果使用.map_err(...)?
,则无需为Error
实现From
。 - Filipe Rodriguesmap_err()
建议尝试以来的情况。到目前为止,我发现唯一的问题是它仍然冗长(与?
相比),但公共 API 非常简洁。现在我想知道为什么我没有看到其他地方使用它。 - jbramleyFrom
不可行时的标准做法,因为这就是map_err
的设计初衷。如果这解决了你的问题,我会发布一个总结解决方案的答案。 - Filipe Rodrigues