rust 为什么我不能为一个泛型类型实现std::error::Error,这个泛型类型是Display+Debug,但是它有一个泛型参数不是Display + Debug?

8fsztsew  于 2022-12-23  发布在  其他
关注(0)|答案(1)|浏览(127)

考虑 Rust 中的以下自定义错误枚举:

#[derive(Debug)]
pub enum MyError<T: FromStr>
    where <T as FromStr>::Err: fmt::Debug
{
    Variant1,
    Variant2,
    FromStrErr(<T as FromStr>::Err),
}

这很管用!

我们可以用impl Display来表示它。因为derive宏,它已经是Debug了。我们甚至可以像预期的那样使用它:

fn main() {
    fn parse(s: &str) -> Result<u32, MyError<u32>> {
        s.parse::<u32>().map_err(MyError::FromStrErr)
    }
    
    println!("{:?}", parse("32"));  //→ Ok(32)
    println!("{:?}", parse("32a")); //→ Err(FromStrErr(ParseIntError { kind: InvalidDigit }))
}

然而,我们 * 不能 * 为它做的是impl std::error::Error

impl<T: FromStr> std::error::Error for MyError<T>
    where <T as FromStr>::Err: fmt::Debug
{ }

即使MyError<T> * 是 * Debug + Display,此impl也会产生错误:T未实现Debug
Playground:https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=61f2e7673143b94f142795a7f00f5148
错误消息中的详细信息包括:

  • 无法使用{:?}T,因为它不实现Debug

这是正确的,但最终是不相关的,因为T根本不在struct中。

*注:MyError<T>需要此选项才能实现Debug

更有趣,但公然不正确,因为MyError<T>已经 * 做 * 实现Debug刚刚好!
显然,有无数种方法可以解决这个错误,但我想知道的是 * 为什么 * 会发生这种情况:为什么在类型上添加一个trait会破坏它呢?
也许是From<…> for Box<dyn …>的邻居不可能发现impl,或者事实上它不是(尚未)在X1 M20 N1 X中但任何与X1 M21 N1 X哪怕是模糊相关的东西总是让我有一种不安的感觉,觉得幕后有某种“魔力”是特定于那个特质的,我不明白,我可能会发现在一个迂回的方式从现在起六个月后,当我已经从任何目前阻碍我。有趣的是,然而,在这个例子中,情况并非如此--把我自己的标记特征作为实验产生了完全相同的错误:

pub trait MyErrorTrait: fmt::Debug + fmt::Display { }

impl<T: FromStr> MyErrorTrait for MyError<T>
    where <T as FromStr>::Err: fmt::Debug
{ }

这个代码段 * 还 * 产生了一个错误,声明T不是Debug(已确认),并且T必须是DebugMyError<T>才能实现Debug-后一部分为false。(代码段包含在 playground gist 中,上面有链接,但注解掉了:(十一.27 -31)

xqk2d5yq

xqk2d5yq1#

这很棘手,MyError确实实现了Debug,但是 * 使用了错误的边界 *。
#[derive]始终创建简单边界:对于每个泛型参数,它创建一个T: Trait边界。在您的示例中,派生的Debug实现如下所示:

impl<T: FromStr> fmt::Debug for MyError<T>
where
    <T as FromStr>::Err: fmt::Debug,
    T: fmt::Debug,
{ ... }

T: fmt::Debug边界由#[derive]生成,另一个从结构体的边界逐字复制。您能发现错误吗?我们需要T: Debug,而我们应该只需要T::Err: Debug!因为您试图为 anyT实现std::error::Error(只要T::Err: Debug),而不仅仅是DebugT,并且Error具有Debug作为超特性,编译器会发出错误-Debug is not implemented *where T: !Debug *。
人们希望创建一个所谓的“完美派生”--一个在类似情况下生成正确边界的派生。同时,您需要手动实现该trait。

相关问题