?`,rust"/>

首页 > 解决方案 > 我怎样才能实现`从?`

问题描述

我正在编写一个新的板条箱,我希望它可用于任何特性的实现(在另一个板条箱中定义)。特征看起来像这样:

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 实现来处理这个问题。当然,这可行,但是:

使我Error对单个类型的泛型成为当今最好的(最惯用的)选择吗?

标签: rust

解决方案


不要From为您的Error枚举实现,而是考虑Result::map_err结合使用?来指定要返回的变体。这甚至适用于使用关联类型的类型上的泛型枚举,例如:

trait TraitA {
  type Error;
  fn do_stuff(&self) -> Result<(), Self::Error>;
}

trait TraitB {
  type Error;
  fn do_other_stuff(&self) -> Result<(), Self::Error>;
}

enum Error<T: TraitA + TraitB> {
  DoStuff(<T as TraitA>::Error),
  DoOtherStuff(<T as TraitB>::Error),
}

fn my_function<T: TraitA + TraitB>(t: T) -> Result<(), Error<T>> {
  t.do_stuff().map_err(Error::DoStuff)?;
  t.do_other_stuff().map_err(Error::DoOtherStuff)?;
  Ok(())
}

在操场上

这里重要的位是Error没有From实现(除了一揽子的),并且变体是使用map_err. 这Error::DoStuff可以解释为fn(<T as TraitA>::Error) -> Error当传递给时map_err。也一样Error::DoOtherStuff

这种方法是可扩展的,无论有多少变体Error以及它们是否是同一类型。阅读函数的人也可能更清楚,因为他们可以找出某个错误来自某个地方,而无需检查From实现以及被转换的类型出现在函数中的位置。


推荐阅读