Macro that matches last argument to function

I need a macro that can extract the last argument to a function. For example, given this code:

string.insert(idx, ch)

I would want extract the ch parameter. Here is what I have created so far:

macro_rules! foo {
    ($($path:ident).* ( $($arg:expr),+ , $cx:expr)  ) => {
        $($path).* ($($arg),+ , $cx);
        // other stuff that uses $cx
    };
}

fn bar() {
    let string = String::from("bob");
    let x = foo!(string.insert(2, 'x'));
}

But when I try and use it get an error about parser ambiguity. Which kind of makes sense since $arg could match the entire argument list without $cx. I am just not sure how to solve it.

error: local ambiguity when calling macro `foo`: multiple parsing options: built-in NTs expr ('cx') or expr ('arg').
   --> src/interpreter.rs:801:35
    |
801 |     let x = foo!(string.insert(2, 'x'));
    |                                   ^^^

You can use recursion in a helper macro:

macro_rules! last {
    ($arg:expr) => { $arg };
    ($head:expr, $($rest:expr),+) => {
        last!($($rest),+)
    };
}

macro_rules! foo {
    ($($path:ident).*($($arg:expr),+)) => {{
        $($path).*($($arg),+);
        println!("cx = {:?}", last!($($arg),+));
    }};
}
1 Like