考虑 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
必须是Debug
,MyError<T>
才能实现Debug
-后一部分为false。(代码段包含在 playground gist 中,上面有链接,但注解掉了:(十一.27 -31)
1条答案
按热度按时间xqk2d5yq1#
这很棘手,
MyError
确实实现了Debug
,但是 * 使用了错误的边界 *。#[derive]
始终创建简单边界:对于每个泛型参数,它创建一个T: Trait
边界。在您的示例中,派生的Debug
实现如下所示:T: fmt::Debug
边界由#[derive]
生成,另一个从结构体的边界逐字复制。您能发现错误吗?我们需要T: Debug
,而我们应该只需要T::Err: Debug
!因为您试图为 anyT
实现std::error::Error
(只要T::Err: Debug
),而不仅仅是Debug
的T
,并且Error
具有Debug
作为超特性,编译器会发出错误-Debug
is not implemented *whereT: !Debug
*。人们希望创建一个所谓的“完美派生”--一个在类似情况下生成正确边界的派生。同时,您需要手动实现该trait。