【发布时间】:2020-12-16 17:56:52
【问题描述】:
我正在编写一个新的 crate,我希望它可以用于任何 trait 的实现(在另一个 crate 中定义)。特征看起来像这样:
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 使得 impls 都适用 - 但我还能如何实现类似的语义?
我查看了其他一些 crate,他们似乎通过将 Error (或等效的)泛型而不是错误类型本身而不是 trait 实现来处理这个问题。当然可以,但是:
- 直到我们有inherent associated types,它更加冗长。我的
T实际上实现了多个特征,每个特征都有自己的Error类型,所以我现在必须返回Result<..., Error<<T as TraitA>::Error, <T as TraitB>::Error>>等类型; - 可以说它的表现力较差(因为与
Trait的关系丢失了)。
让我的Error 泛化单个类型是当今最好的(最惯用的)选项吗?
【问题讨论】:
-
您可以使用
.map_err(Error::TraitError)?而不是简单的?,而不是实现From,这样错误类型已经是Error而不是T::Error和?将使用用T = Error覆盖impl<T> From<T> for T。 -
是的,这行得通,并且考虑它是它可以工作的唯一方式。
Fromtrait 只是作用于一个类型,它不知道它来自哪里。如果TraitA::Error和TraitB::Error具有相同的类型会怎样?我仍然必须将Error设为通用,但它可以只针对一种类型通用,而不是每个单独的特征。 -
您不能将
T与T: TraitA + TraitB绑定,并在Error中有两个变体,一个TraitA(<T as TraitA>::Error),另一个为TraitB(<T as TraitB>::Error)和.map_err(...)?,具体取决于使用哪个特征?即使它们具有相同的类型,它们也会有不同的变体,如果使用.map_err(...)?,则不需要为Error实现From。 -
是的,这正是我所拥有的,因为尝试了您的
map_err()建议。到目前为止我发现的唯一问题是它仍然很冗长(与?相比),但公共 API 非常简洁。现在我想知道为什么我没有看到它在其他地方使用过。 -
需要详细说明要返回的变体,我相信这是实现
From不可行的标准做法,因为它是map_err的设计目的。如果这解决了您的问题,我将发布一个总结解决方案的答案。
标签: rust