2

I want to generate a static array (doing this at runtime is not an option) with help of a macro.

My attempts are

macro_rules! test {
    ($($data:expr),*)   => {
    [ test!(@xform $($data),*) ]
    };

    (@xform)            => { };
    (@xform $a:expr)        => { be32($a) };
    (@xform $a:expr, $($data:expr),*)   => { be32($a), test!(@xform $($data),*) };
}

// just for simplicity...
const fn be32(v: u32) -> u32 { v + 1 }

static FILE_HEADER_0: [u32;2] = test!(1, 2);
static FILE_HEADER_1: [u32;2] = [be32(1), be32(2)];

but this fails with

error: macro expansion ignores token `,` and any following
 --> src/lib.rs:8:52
  |
3 |     [ test!(@xform $($data),*) ]
  |       ------------------------- help: you might be missing a semicolon here: `;`
  |       |
  |       caused by the macro expansion here
...
8 |     (@xform $a:expr, $($data:expr),*)    => { be32($a), test!(@xform $($data),*) };
  |                                                       ^
  |
  = note: the usage of `test!` is likely invalid in expression context

I expect that FILE_HEADER_0 is generated like FILE_HEADER_1

Is this possible with normal macro_rules! or do I have to use proc_macro?

3
  • 4
    Something like this playground? Commented Sep 7, 2022 at 10:35
  • Hopefully soon you don't even need a macro, and std::array::from_fn can be const when used with a const fn. Commented Sep 7, 2022 at 17:56
  • There's also this crate: docs.rs/array-const-fn-init/latest/array_const_fn_init Commented Sep 7, 2022 at 17:58

1 Answer 1

1

Your macro expands to multiple comma-separated expressions, and this is invalid. It has to generate only one.

The usual solution to that is push-down accumulation: accumulate the resulting array in the macro and keep adding elements to it:

macro_rules! test {
    ($($data:expr),*) => {
        test!(@xform [] $($data),*)
    };

    (@xform $arr:tt) => { $arr };
    (@xform [ $($arr:tt)* ] $a:expr) => {
        test!(@xform [ $($arr)* be32($a) ])
    };
    (@xform [ $($arr:tt)* ] $a:expr, $($data:expr),*) => {
        test!(@xform [ $($arr)* be32($a), ] $($data),*)
    };
}

In the first expansion we will have test!(@xform [] 1, 2). This will reach the last arm and expand to test!(@xform [ be(1), ] 2). This will expand (by the third arm) to test!(@xform [ be(1), be(2) ]) which will finally expand to [be(1), be(2)] by the second arm.

In this case, however, you don't need complex solutions. A simple macro will do:

macro_rules! test {
    ($($data:expr),*) => {
        [ $( be32($data) ),* ]
    };
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.