divinites
divinites

Reputation: 109

Can Rust macros deal with multiple-layer nested expressions?

The following code is a simple test of macro_rule!'s limit. It correctly prints [1, 2] as the target.

macro_rules! test {
    ($target:tt) => {
        println!("{:?}", $target);
    };
    ($target:tt, $($rest:tt),*) => {
        test!($target)
    };
    ([$target:tt, $($inner_rest:tt),*],$($rest:tt),*) => {
        test!($target)
    }
}

fn main() {
    test![[1, 2], [[5, 6], 3], 4];
}

However, if I change [[1,2] to [[1,2],3], the compiler complains:

<anon>:16:15: 16:16 error: mismatched types:
 expected `[_; 2]`,
    found `_`
(expected array of 2 elements,
    found integral variable) [E0308]
<anon>:16  test![[[1,2],3],[[5,6],3],4];
                        ^
<std macros>:2:25: 2:56 note: in this expansion of format_args!
<std macros>:3:1: 3:54 note: in this expansion of print! (defined in <std macros>)
<anon>:4:5: 4:30 note: in this expansion of println! (defined in <std macros>)
<anon>:7:6: 7:20 note: in this expansion of test! (defined in <anon>)
<anon>:16:2: 16:31 note: in this expansion of test! (defined in <anon>)
<anon>:16:15: 16:16 help: see the detailed explanation for E0308
error: aborting due to previous error
playpen: application terminated with error code 101

Is there a way to let the macro recursively deal with brackets and print the first item? I have studied the example given by the Rust documentation, but still have no idea about how to implement it in my case.

Upvotes: 2

Views: 993

Answers (1)

DK.
DK.

Reputation: 59135

You asked it to parse an expression, so that's exactly what it tried to do.

The first thing it sees is [[1, 2], 3] which is invalid because it's an array literal where the first element is of type [i32; 2] and the second element is of type i32. So, it fails.

If you want to macro to deal with recursive syntax, you're going to have to write it to handle it. I have no idea what you're trying to do ("deal with bracket" how?), so I can't offer a concrete example. Try adding an extra rule before the existing ones that explicitly deals with [[$($stuff:tt)*] .... Rules are processed top-to-bottom, so you need to put the most specific rule first. The following prints out [1, 2]:

macro_rules! test {
    ([$target:tt, $($inner_rest:tt),*], $($rest:tt),*) => {
        test!($target)
    };
    ($target:tt) => {
        println!("{:?}", $target);
    };
    ($target:tt, $($rest:tt),*) => {
        test!($target)
    };
}

fn main() {
    test![[[1,2],3],[[5,6],3],4];
}

Upvotes: 3

Related Questions