CircArgs
CircArgs

Reputation: 650

Rust macro error: local ambiguity: multiple parsing options

The following rust code does not compile because of the macro error

error: local ambiguity: multiple parsing options: built-in NTs stmt ('s') or 1 other option. 

macro A is fine. Macro B shows the error.

macro_rules! A {
    ($x: ident, $($s: stmt)*) => {
        println!("hello");
    };
}

macro_rules! B {
    ($x: ident, $($s: stmt)*; $e: expr) => {
        println!("hello");
    };
}

fn main() {
    A![my_name, let x=5];
    B![my_name, let x=5; 5];
}

This minimal reproducible example in B is exactly what I need. I want the macro to accept multiple let statements and terminate by some other expression.

What is the ambiguity that is being referred to?

Is there a way around it?

Of those tokens accepted after statement fragments I have tried several combinations yet none appear to make a difference. Neither does swapping the statement with a token tree.

Upvotes: 1

Views: 2201

Answers (1)

mcarton
mcarton

Reputation: 29981

Expressions are statements, so $($s: stmt)*; $e: expr is ambiguous because the compiler can't decide between using s or e when it encounters one.

Since you only expect bindings, you can easily expand them yourself:

macro_rules! A {
    ($x: ident, $($s: stmt)*) => {
        println!("hello");
    };
}

macro_rules! B {
    ($x: ident, $(let $p:pat = $v:expr)*; $e: expr) => {
        $(let $p = $v);*
        println!("hello: {}", $e);
    };
}

fn main() {
    A![my_name, let x=5];
    B![my_name, let x=5; x+2];
}

Note that this doesn't support including types in the binding (let a: i32 = 42;) because pat can't be followed by :.

Upvotes: 6

Related Questions