如何将自定义失败与失败包匹配

问题描述 投票:0回答:1

我正在努力了解如何使用failure箱子。它作为不同类型的标准错误的统一工作非常出色,但在创建自定义错误(Fails)时,我不明白如何匹配自定义错误。例如:

use failure::{Fail, Error};

#[derive(Debug, Fail)]
pub enum Badness {
  #[fail(display = "Ze badness")]
  Level(String)
}

pub fn do_badly() -> Result<(), Error> {
  Err(Badness::Level("much".to_owned()).into())
}

#[test]
pub fn get_badness() {
  match do_badly() {
    Err(Badness::Level(level)) => panic!("{:?} badness!", level),
    _ => (),
  };
}

失败了

error[E0308]: mismatched types
  --> barsa-nagios-forwarder/src/main.rs:74:9
   |
73 |   match do_badly() {
   |         ---------- this match expression has type `failure::Error`
74 |     Err(Badness::Level(level)) => panic!("{:?} badness!", level),
   |         ^^^^^^^^^^^^^^^^^^^^^ expected struct `failure::Error`, found enum `Badness`
   |
   = note: expected type `failure::Error`
              found type `Badness`

如何制定与特定自定义错误匹配的模式?

error-handling rust
1个回答
1
投票

You need to downcast the Error

当您从某种实现failure::Error特征的类型创建Fail时(通过frominto,就像您一样),您暂时隐藏有关您从编译器中包装的类型的信息。它不知道ErrorBadness - 因为它也可以是任何其他Fail类型,这就是重点。你需要提醒编译器这个,这个动作叫做downcasting。 failure::Error有三种方法:downcastdowncast_refdowncast_mut。在你向下转换之后,你可以像往常一样对结果进行模式匹配 - 但是你需要考虑到向下转换本身可能会失败的可能性(如果你试图向下转换为错误的类型)。

以下是downcast的外观:

pub fn get_badness() {
    if let Err(wrapped_error) = do_badly() {
        if let Ok(bad) = wrapped_error.downcast::<Badness>() {
            panic!("{:?} badness!", bad);
        }
    }
}

(在这种情况下可以合并两个if lets)。

如果需要测试多个错误类型,这很快就会变得非常不愉快,因为downcast消耗它被调用的failure::Error(因此如果第一个失败,你不能在同一个变量上尝试另一个downcast)。遗憾的是,我无法想出一个优雅的方法来做到这一点。这是一个不应该真正使用的变体(panic!中的map是值得怀疑的,做其他事情会有很多尴尬,我甚至不想考虑更多的情况而不是两个):

#[derive(Debug, Fail)]
pub enum JustSoSo {
    #[fail(display = "meh")]
    Average,
}

pub fn get_badness() {
    if let Err(wrapped_error) = do_badly() {
        let e = wrapped_error.downcast::<Badness>()
            .map(|bad| panic!("{:?} badness!", bad))
            .or_else(|original| original.downcast::<JustSoSo>());
        if let Ok(so) = e {
            println!("{}", so);
        }
    }
}

如果你真的想从所有可能的\相关错误中产生一些相同类型的值,那么or_else链应该可以正常工作。如果对原始错误的引用对你来说很好,也可以考虑使用非消耗方法,因为这样你就可以制作一系列if let块,每个downcast尝试一个。

An alternative

不要将您的错误放入failure::Error,将它们作为变体放入自定义枚举中。它是更多样板,但你可以获得无痛模式匹配,编译器也可以检查它是否合理。如果你选择这样做,我会推荐derive_more crate,它能够为这些枚举得到From; snafu看起来也很有趣,但我还没有尝试过。在最基本的形式中,这种方法看起来像这样:

pub enum SomeError {
    Bad(Badness),
    NotTooBad(JustSoSo),
}

pub fn do_badly_alt() -> Result<(), SomeError> {
    Err(SomeError::Bad(Badness::Level("much".to_owned())))
}

pub fn get_badness_alt() {
    if let Err(wrapper) = do_badly_alt() {
        match wrapper {
            SomeError::Bad(bad) => panic!("{:?} badness!", bad),
            SomeError::NotTooBad(so) => println!("{}", so),
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.