为什么特征类型 `Box<dyn Error>` 会出现“Sized is not Implemented”错误,但 `async fn() -> Result<(), Box<dyn Error>>` 却可以工作?

cre*_*lem 7 rust

我\xe2\x80\x99ve以下简化代码。

\n
use async_trait::async_trait; // 0.1.36\nuse std::error::Error;\n\n#[async_trait]\ntrait Metric: Send {\n    type Output;\n    type Error: Error;\n\n    async fn refresh_metric(&mut self) -> Result<Self::Output, Self::Error>;\n}\n\n#[derive(Default)]\nstruct StaticMetric;\n\n#[async_trait]\nimpl Metric for StaticMetric {\n    type Output = ();\n    type Error = Box<dyn Error>;\n\n    async fn refresh_metric(&mut self) -> Result<Self::Output, Self::Error> {\n        Ok(())\n    }\n}\n\nstruct LocalSystemData<T> {\n    inner: T,\n}\n\nimpl<T> LocalSystemData<T>\nwhere\n    T: Metric,\n    <T as Metric>::Error: \'static,\n{\n    fn new(inner: T) -> LocalSystemData<T> {\n        LocalSystemData { inner }\n    }\n\n    async fn refresh_all(&mut self) -> Result<(), Box<dyn Error>> {\n        self.inner.refresh_metric().await?;\n        Ok(())\n    }\n}\n\n#[tokio::main]\nasync fn main() -> Result<(), Box<dyn Error>> {\n    let mut sys_data = LocalSystemData::new(StaticMetric::default());\n    sys_data.refresh_all().await?;\n\n    Ok(())\n}\n
Run Code Online (Sandbox Code Playgroud)\n

操场

\n

编译器抛出以下错误

\n
use async_trait::async_trait; // 0.1.36\nuse std::error::Error;\n\n#[async_trait]\ntrait Metric: Send {\n    type Output;\n    type Error: Error;\n\n    async fn refresh_metric(&mut self) -> Result<Self::Output, Self::Error>;\n}\n\n#[derive(Default)]\nstruct StaticMetric;\n\n#[async_trait]\nimpl Metric for StaticMetric {\n    type Output = ();\n    type Error = Box<dyn Error>;\n\n    async fn refresh_metric(&mut self) -> Result<Self::Output, Self::Error> {\n        Ok(())\n    }\n}\n\nstruct LocalSystemData<T> {\n    inner: T,\n}\n\nimpl<T> LocalSystemData<T>\nwhere\n    T: Metric,\n    <T as Metric>::Error: \'static,\n{\n    fn new(inner: T) -> LocalSystemData<T> {\n        LocalSystemData { inner }\n    }\n\n    async fn refresh_all(&mut self) -> Result<(), Box<dyn Error>> {\n        self.inner.refresh_metric().await?;\n        Ok(())\n    }\n}\n\n#[tokio::main]\nasync fn main() -> Result<(), Box<dyn Error>> {\n    let mut sys_data = LocalSystemData::new(StaticMetric::default());\n    sys_data.refresh_all().await?;\n\n    Ok(())\n}\n
Run Code Online (Sandbox Code Playgroud)\n

我不太确定我是否正确理解了这个问题。

\n

I\xe2\x80\x99m 使用Box<dyn Error>是因为我不\xe2\x80\x99t 有具体类型,并且装箱错误可以处理所有错误。在我的实现中LocaSystemData,我添加了<T as Metric>::Error: \'static(编译器给了我这个提示,而不是我的想法)。添加\'static要求后,编译器抱怨大小未知。发生这种情况是因为类型的大小\'static应该在编译时始终已知,因为静态的含义。

\n

我更改了该Metric特征,以便摆脱type Error;现在我有以下异步特征函数并且我的代码可以编译

\n

操场

\n
async fn refresh_metric(&mut self) -> Result<Self::Output, Box<dyn Error>>;\n
Run Code Online (Sandbox Code Playgroud)\n

为什么我的第二个版本可以编译而第一个版本不能编译?对于我作为一个人来说,代码的作用完全相同。我觉得我欺骗了编译器:-)。

\n

E_n*_*ate 5

该错误消息有点误导,因为它代表了类型约束解析期间出现的中间问题。无需异步即可重现相同的问题。

use std::error::Error;

trait Metric {
    type Error: Error;
}

struct StaticMetric;

impl Metric for StaticMetric {
    type Error = Box<dyn Error>;
}
Run Code Online (Sandbox Code Playgroud)

问题的根源在于Box<dyn Error>不落实std::error::Error。正如From<Box<E>>forBox<dyn Error>的实现所指定的,内部类型E也必须是Sized.

impl<'a, E: Error + 'a> From<E> for Box<dyn Error + 'a>
Run Code Online (Sandbox Code Playgroud)

因此,Box<dyn Error>不应分配给关联类型Metric::Error

除了完全摆脱关联类型之外,还可以通过引入您自己的新错误类型(可以流入主函数)来解决此问题。

#[derive(Debug, Default)]
struct MyErr;

impl std::fmt::Display for MyErr {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        f.write_str("I AM ERROR")
    }
}
impl Error for MyErr {}

#[async_trait]
impl Metric for StaticMetric {
    type Output = ();
    type Error = MyErr;

    async fn refresh_metric(&mut self) -> Result<Self::Output, Self::Error> {
        Ok(())
    }
}
Run Code Online (Sandbox Code Playgroud)

操场

也可以看看:

  • @createproblem `?` 语法实际上并不要求类型实现 `Error`。它需要通过“From”从给定的错误变体类型转换为函数签名定义的错误。这是[另一个示例](https://play.rust-lang.org/?version=stable&amp;mode=debug&amp;edition=2018&amp;gist=c7817553611fe958a28464c83036fd45),它不使用任何错误类型。 (2认同)
  • 我已经用更多细节更新了问题。在另一种情况下,这是因为“From”的实现中存在一个怪癖,它不允许您将“Box&lt;dyn Error&gt;”转换为另一个“Box&lt;dyn Error&gt;”。[此处](https://github.com/rust-lang/rust/pull/58974)尝试解决此问题,但它会对语言产生严重影响。 (2认同)