[Solved] Optional trailing macro delimiter?

Hi there! I’m fairly new to rust and trying to wrap my head around macros. I’ve read both the Rust Programming Language book’s section on macros as well as went through http://danielkeep.github.io/tlborm/book/pat-trailing-separators.html and still can’t seem to quite understand what I’m doing wrong here.

I have a bit of code like this:

macro_rules! matrix {
    ( $( $($x:expr),* ;)* ) => {
        {
            vec![
                $(
                    vec![$($x),*]
                ),*
            ]
        }
    };
}

fn main() {
    let m = matrix![
        1, 2, 3;
        4, 5, 6;
        7, 8, 9;
    ];
    println!("{:?}", m);
}

I was wondering if it’s possible to have the final semicolon optional? It’s not a deal breaker to have them always required. It’s more of me trying to understand the language and how macros work.

Would I need to create a 2nd branch? Some of the macro and repetition syntax is still voodoo to me.

In edition 2018, you can use $(<your separator>)?. See https://doc.rust-lang.org/edition-guide/rust-2018/macros/at-most-once.html

Hi, I think this works:

macro_rules! matrix {
    ( $( $($x:expr),+ $(;)?)* ) => {
        {
            vec![
                $(
                    vec![$($x),*]
                ),*
            ]
        }
    };
    ($($sc: tt)+) => {
        vec![
            $({
                $sc
                vec![]
            }),+
        ]
    }
}

The second branch is needed to handle this case:

matrix![
    ;
    ;
    ;
]

I’m just afraid the second pattern is a little too permissive.

I didn’t noticed it but it just work.
EDIT: nevermind, I missed the empty row in the output. I’ve tested it with $(;)? pattern at the end, but it doesn’t work in the same reason why the below compiles.

macro_rules! matrix {
    ( $( $($x:expr),*);*) => {
        {
            vec![
                $(
                    vec![$($x),*]
                ),*
            ]
        }
    };
}

fn main() {
    let m = matrix![
        1, 2, 3;
        4, 5, 6;
        7, 8, 9;
    ];
    let m2 = matrix![
        1, 2, 3;
        4, 5, 6;
        7, 8, 9
    ];
    println!("{:?}, {:?}", m, m2);
}

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=c24098af4cae31a91fd18ff8562079e8

This is how I would do it:

macro_rules! matrix {(
    $(
        $($x:expr),+
    );* $(;)?
) => (
    [$(
        [$($x),*],
    )*]
)}

Instead of using vec!tors, it uses fixed-size arrays, thus statically enforcing that all rows have the same length :slight_smile:

1 Like

Ah I do remember running into that odd case with an extra empty row as well!

So now the question is, how can we distinguish a sequence with trailing semicolon and a sequence that ends with empty vector and without trailing sequence.

Thanks! That’s also a great idea to use fixed-size arrays too! Appreciate it!

Now for me to try and internalize what the syntax is actually doing :slight_smile:

By using the + repetition operator rows cannot be empty

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.