在宏中匹配多个枚举类型

match multiple enum types in macro

我有一个宏,用于将 Box<dyn error::Error> 与不同的枚举相匹配。对于我现在使用的版本,我需要在 if else 链中针对我想要匹配的每种错误类型重复调用它。

#[macro_export]
macro_rules! dyn_match {
    ($e:expr, $($pat:pat => $result:expr),*) => (
        if let Some(e) = $e.downcast_ref() {
            match e {
                $(
                    $pat => {$result; true}
                ),*
            }
        } else {false}
    );
}

...
//fn example(x: u32) -> Result<u32, Box<dyn error::Error>>;
match example(9) {
    Ok(_) => Ok(()),
    Err(e) => {
        if dyn_match!(e, 
            ExampleError1::ThisError(2) => panic!("it was 2!"),
            i => {}
        ) {Ok(())}
        else if dyn_match!(e,
            ExampleError2::ThatError(8) => panic!("it was 8!"),
            ExampleError2::ThatError(9) => panic!("it was 9!"),
            i => {}
        ) {Ok(())}
        else {panic!("{}",e)}
    }
}
...

我的目标是做到这一点,这样我就可以将多种错误类型传递给它,它会为每种类型进行单独的匹配。唯一的问题是,要将类型从 Box<dyn error::Error> 转换为其原始错误类型,我需要对其调用 downcast_ref()。当传递的每个错误都是同一类型时,这工作正常,但是当我尝试为每个提供的手臂调用一个单独的匹配块时,它会抛出错误 cannot infer type for type parameter 'T' declared on the associated function 'downcast_ref'

#[macro_export]
macro_rules! new_dyn_match {
    ($e:expr, $($pat:pat => $result:expr),*) => (
        {
            $(
                if let Some(e) = $e.downcast_ref() {   //cannot infer type for type parameter `T` declared on the associated function `downcast_ref`
                    if let $pat = e {$result}
                }
            )*   
        }
    );
}

...
//fn example(x: u32) -> Result<u32, Box<dyn error::Error>>;
match example(9) {
    Ok(_) => Ok(()),
    Err(e) => {
        new_dyn_match!(e, 
            ExampleError1::ThisError(2) => panic!("it was 2!"),
            ExampleError2::ThatError(8) => panic!("it was 8!"),
            ExampleError2::ThatError(9) => panic!("it was 9!"),
            i => {}
        );
        Ok(())
    }
}
...

我是宏的新手,但看起来,在编译期间展开时,它应该能够看到每个匹配项将与哪种类型进行比较,并隐式向下转换为该类型。我真的不确定如何解决这个问题。有什么想法吗?

知道了!

#[macro_export]
macro_rules! dynmatch {
    ($e:expr, $(type $ty:ty {$(arm $pat:pat => $result:expr),*, _ => $any:expr}),*, _ => $end:expr) => (
        $(
            if let Some(e) = $e.downcast_ref::<$ty>() {
                match e {
                    $(
                        $pat => {$result}
                    )*
                    _ => $any
                }
            } else
        )*
        {$end}
    );
}
...
        let _i = match example(3) {
            Ok(i) => i,
            Err(e) => {
                dynmatch!(e,                                                    //the dynamic error to be matched
                    type ExampleError1 {                                        //an error group
                        arm ExampleError1::ThisError(2) => panic!("it was 2!"), //arm [pattern] => {code}
                        _ => panic!("{}",e)                                     //_ => {code}
                    },
                    type ExampleError2 {
                        arm ExampleError2::ThatError(8) => panic!("it was 8!"),
                        arm ExampleError2::ThatError(9) => 9,
                        _ => panic!("{}",e)
                    }, 
                    _ => panic!("{}",e)                                         //what to do if error group isn't found
                )
            }
        };
...

将解决方案归功于 this crate。 另外,如果有人对更好的解决方案有任何想法,请告诉我!