0
votes

i have a macro thats used for matching a Box<dyn error::Error> with different enums. with the version im using now, i need to call it repeatedly in an if else chain for each error type i want to match against.

#[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)}
    }
}
...

my goal is to make it so i can pass multiple error types to it and it'll do an individual match for each type. the only issue is, to convert the type from Box<dyn error::Error> to its original error type i need to call downcast_ref() on it. this works fine when every error passed is of the same type, but when i attempt to call a separate match block for each supplied arm it throws the error 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(())
    }
}
...

im new to macros but it seems like, upon expansion during compile, it should be able to see which type each match would be compared against and implicitly downcast to that type. im really not sure how to fix this. any ideas?

1

1 Answers

0
votes

got it!

#[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
                )
            }
        };
...

credit to this crate for the solution. also, if anyone has any ideas on a better solution please let me know!