请考虑以下以Rust为单位的自定义错误枚举:

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

100

一个人可以买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 }))
}

然而,cannot所做的是impl std::error::Error:

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

Even though MyError<T> is Debug + Display, this impl produces the error: T doesn't implement Debug.
Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=61f2e7673143b94f142795a7f00f5148

错误消息中的详细信息包括:

  • T cannot be formatted using {:?} because it doesn't implement Debug
    This is true but ultimately irrelevant because T is not in the struct at all.
  • note: required for MyError<T> to implement Debug
    More interesting but blatantly incorrect because MyError<T> already did implement Debug just fine!

显然,有无数种方法可以解决这个错误,但我想知道的是why这种情况正在发生:为什么在类型上添加一个特征会 destruct 它?

也许是因为From<…> for Box<dyn …>中几乎不可能发现的impl,或者它(目前还)不是core中的,但任何与std::error::Error有隐约关联的东西总是让我不安地感觉到,幕后有一些特定于这一特征的"魔力",我不明白,当我从目前阻碍我的事情继续前进的时候,我可能会在六个月后发现.然而,有趣的是,在这种情况下情况并非如此--将我自己的标记性状作为实验产生了完全相同的错误:

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

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

该代码片段also生成一个错误,声明T不是Debug(已确认),并且T必须是Debug才能使MyError<T>实现Debug--后一部分为假.(playground gist中包含的片段,上面链接,但已注释掉:11.27-31)

推荐答案

这很棘手.诚然,MyError执行Debug,但with the wrong bounds执行.

#[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]生成的,另一个是从 struct 的界限中逐字复制的.你能找出错误吗?我们需要T: Debug个,而我们应该只需要T::Err: Debug个!由于您try 为anyT实现std::error::Error(只要T::Err: Debug),而不仅仅是DebugT,并且Error具有Debug作为上属性,因此编译器会发出错误-Debug未实现where 112.

人们希望创造一个所谓的"完美派生"--一种在这样的情况下产生正确界限的派生.同时,您需要手动实现特征.

Rust相关问答推荐

在rust中如何修改一个盒装函数并将其赋回?

如何在Rust中实现Functor trait?

使用 struct 外部的属性来改变 struct 的原始方式

如何在Bevy/Rapier3D中获得碰撞机的计算质量?

为什么reqwest以文本形式下载二进制文件?

在使用#[NO_STD]时,如何在Rust中收到紧急消息?

Tokio_Postgres行上未显示退回特性的生存期,且生命周期 不够长

当发送方分配给静态时,Tokio MPSC关闭通道

失真图像图形捕获Api

Tokio';s io::用Cursor拆分<;Vec<;u8>>;赢得';t get the full writted data

在 Rust 中,在需要引用 self 的 struct 体方法中使用闭包作为 while 循环条件

Boxing 如何将数据从堆栈移动到堆?

需要一个有序向量来进行 struct 初始化

decltype、dyn、impl traits,重构时如何声明函数的返回类型

有什么办法可以追踪泛型的单态化过程吗?

为什么可以在迭代器引用上调用 into_iter?

为什么在 macOS / iOS 上切换 WiFi 网络时 reqwest 响应会挂起?

判断 is_ok 后重用结果

通用函数中的生命周期扣除和borrow (通用测试需要)

传递 Option<&mut T> 时何时需要 mut