antoyo
antoyo

Reputation: 11933

How can I use a macro to create an array of function names starting from a collection of function definitions?

I would like to create an array in a macro to transform something like:

let array = create_array!(
    fn test() -> i32 { }
    fn test1() { }
);

into

let array = [test, test1];

I tried this:

macro_rules! create_array {
    () => {
    };
    (fn $func_name:ident () -> $return_type:ty $block:block $($rest:tt)*) => {
        $func_name,
        create_array!($($rest)*);
    };
    (fn $func_name:ident () $block:block $($rest:tt)*) => {
        $func_name,
        create_array!($($rest)*);
    };
}

but it fails with the following error:

error: macro expansion ignores token `,` and any following
  --> src/main.rs:11:19
   |
11 |         $func_name,
   |                   ^
   |
note: caused by the macro expansion here; the usage of `create_array!` is likely invalid in expression context
  --> src/main.rs:27:18
   |
27 |     let array = [create_array!(
   |

I also tried this:

macro_rules! create_array {
    ($(fn $func_name:ident () $( -> $return_type:ty )* $block:block)*) => {
        [$($func_name),*]
    };
}

but it fails with:

error: local ambiguity: multiple parsing options: built-in NTs block ('block') or 1 other option.
  --> src/main.rs:22:19
   |
22 |         fn test() -> i32 { }
   |

So how can I create an array in such a case?

Upvotes: 1

Views: 975

Answers (1)

dtolnay
dtolnay

Reputation: 11043

The parsing ambiguity of -> vs $:block has been resolved as of Rust 1.20, so the second version you tried will now work as intended.

macro_rules! create_array {
    ($(fn $func_name:ident () $(-> $return_type:ty)* $block:block)*) => {
        [$($func_name),*]
    };
}

fn main() {
    let test = "TEST";
    let test1 = "TEST1";

    let array = create_array! {
        fn test() -> i32 {}
        fn test1() {}
    };

    println!("{:?}", array);
}

Upvotes: 3

Related Questions