Tricky macro problem: Accept a sequence of possibly empty items (e.g. 1,2,,3) AND handle trailing commas properly

โš“ Rust    ๐Ÿ“… 2025-10-21    ๐Ÿ‘ค surdeus    ๐Ÿ‘๏ธ 3      

surdeus

Hi,

In the library that I am working on, I would like to store sequences of sequences of things in compactly in memory by prepending each subsequence with its length. (In reality the things are symbols, and I use a union type for the element, and everything is properly encapsulated, but for simplicity integers will do here.)

So, for example, in the program below, the macro invocation seq_of_seqs![-1 -2, , -3] evaluates to

[
    2,  // Number of elements in first subsequence
    -1, // First element of first subsequence
    -2, // Second element of first subsequence
    0,  // Number of elements in second subsequence
    1,  // Number of elements in third subsequence
    -3, // First and only element of third subsequence
]

Here is the full code

// Based on
// https://veykril.github.io/tlborm/decl-macros/building-blocks/counting.html#bit-twiddling
#[doc(hidden)]
#[macro_export]
macro_rules! _count_exprs {
    () => { 0 };
    ($single:expr) => { 1 };
    ($odd:expr, $($a:expr, $b:expr),*) => { ($crate::_count_exprs!($($a),*) << 1) | 1 };
    ($($a:expr, $even:expr),*) => { $crate::_count_exprs!($($a),*) << 1 };
}

#[macro_export]
macro_rules! seq_of_seqs {
    () => {{
        [0; 0]
    }};
    ($($($number:literal )*),* ) => {{
        [
            $(
                $crate::_count_exprs!($($number),*)
                $(, $number )*
            ),*
        ]
    }};
}

fn main() {
    dbg!(seq_of_seqs![-1 -2, , -3]);
}

This works, but the problem is that in accordance with Rust conventions, I would like to ignore trailing commas. I.e. I would like seq_of_seqs![-1 -2, , -3,] to evaluate to the same thing as above, but since I also need to support empty subsequences, seq_of_seqs![-1 -2, , -3,,] should evaluate to

[
    2,  // Number of elements in first subsequence
    -1, // First element of first subsequence
    -2, // Second element of first subsequence
    0,  // Number of elements in second subsequence
    1,  // Number of elements in third subsequence
    -3, // First and only element of third subsequence
    0,  // Number of elements in fourth subsequence
]

Note that this is conceptually coherent. (Incidentally, I discovered that this is how JavsScript handles array literals.)

I know how to either

  • accept empty subsequences without handling trailing commas properly (thatโ€™s the example above),

  • or accept trailing commas when no empty subsequences are allowed.

But despite a lot of trying and searching, I have not found a solution to have both. Itโ€™s probably not helpful if I list all the things that I have tried (like explicitly matching sequences of commas with nothing in-between), because none of them have worked.

I would be grateful for any ideas or suggestions.

2 posts - 2 participants

Read full topic

๐Ÿท๏ธ Rust_feed