Search code examples
rustmacro-rules

Difference between metavariables within Rust macro_rules


I wrote a macro but now I am trying to extend it so I can pass it a function. I read the Rust Reference and I don't really understand metavariables. I feel like everything I want is going to be an expression, but how can I pass error to an expression without breaking everything?

macro_rules! unwrap_or_return {
    ( $a:expr, $b:expr ) => {
        match $a {
            Ok(x) => x,
            Err(_) => return Err($b),
        }
    };
    // new part
    ( $a:expr, $fun:FUNCTION ) => {
        match $a {
            Ok(x) => x,
            Err(error) => return Err($fun(error)),
        }
    };
}

Solution

  • macro_rules! unwrap_or_return {
        // This needs to go fist since is's more specific version of expression branch below would
        // make this one unreachable if preceded.
        //
        // What I ve done here is something that looks like a closure to the user and is probably what
        // you want. The metavariables are something predefined by rust and they simply capture some
        // portion of syntax. In this case, $p captures a closure or function parameter syntax which we
        // can inject into error pattern. Since the $p is written  by the user, the identifier is
        // visible to to other code made by user.
        ($a:expr, |$p:pat_param| $body:expr) => {
            match $a {
                Ok(x) => x,
                Err($p) => return Err($body.into()),
            }
        };
    
        ($a:expr, $b:expr) => {
            match $a {
                Ok(x) => x,
                Err(_) => return Err($b.into()),
            }
        };
    }
    
    fn example() -> Result<(), String> {
        let a = Ok::<usize, String>(1);
        let b = Err("error".to_string());
    
        let _x = unwrap_or_return!(a, "error");
        let _y = unwrap_or_return!(b, |e| format!("error: {}", e));
    
        Ok(())
    }