1

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?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
antoyo
  • 11,097
  • 7
  • 51
  • 82
  • You can't do the first [because you have to expand to syntactically valid code](http://stackoverflow.com/a/36259524/155423). Also, there's no `[` or `]` that I can see... – Shepmaster Oct 11 '16 at 20:58
  • Well, for the first macro, you would need to use `let array = [create_array!( fn test() -> i32 { } fn test1() { } )];` if it was possible. – antoyo Oct 11 '16 at 21:01
  • You probably should include the intended usage of each macro for completeness. – Shepmaster Oct 11 '16 at 21:03
  • 1
    `[test, test1]` → this wouldn't compile, the two functions have different types. – mcarton Oct 11 '16 at 21:13
  • @mcarton The macro does not generate the functions, so the identifiers will have to come from somewhere else. – antoyo Oct 11 '16 at 21:28
  • @Shepmaster It is a bit complicated, so I'll post a brief description here. It is used to generate a proxy object for dbus. The macro generate `const` objects that represent the dbus methods; these const objects are named by the function name. I need to create another `const` object that holds an array of these methods: hence my need for this macro. If there is another way to achieve the desired result, that would be appreciated too. – antoyo Oct 11 '16 at 21:31

1 Answers1

3

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);
}
dtolnay
  • 9,621
  • 5
  • 41
  • 62