Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use variadic macros to call nested constructors?

Tags:

macros

rust

I'm trying to create a macro in Rust that lets me write

make_list!(1, 2, 3)

instead of

Node::new(1, Node::new(2, Node::new(3, None)))

which should work for an arbitrary number of "parameters" including zero. This is what I have so far:

macro_rules! make_list(
    () => (
        None
    );
        ( $x:expr, $( $more:expr ),* ) => (
        Node::new($x, make_list!( $( $more ),* ))
    )
);

but I get the following error:

error: unexpected end of macro invocation
  --> src/main.rs:19:42
   |
19 |             Node::new($x, make_list!( $( $more ),* ))
   |                                          ^^^^^

I can't make much sense of this. From what I can tell, it should work. What did I do wrong?

The complete code:

type List<T> = Option<Box<Node<T>>>;

struct Node<T> {
    value: T,
    tail: List<T>,
}

impl<T> Node<T> {
    fn new(val: T, tai: List<T>) -> List<T> {
        Some(Box::new(Node::<T> {
            value: val,
            tail: tai,
        }))
    }
}

macro_rules! make_list(
    () => (
        None
    );
    ( $x:expr, $( $more:expr ),* ) => (
        Node::new($x, make_list!( $( $more ),* ))
    )
);

fn main() {
    let _list: List<i32> = make_list!(1, 2, 3, 4, 5, 6, 7, 8, 9);
}
like image 621
sellibitze Avatar asked Jul 01 '14 14:07

sellibitze


People also ask

How to use variadic macro?

To use variadic macros, the ellipsis may be specified as the final formal argument in a macro definition, and the replacement identifier __VA_ARGS__ may be used in the definition to insert the extra arguments. __VA_ARGS__ is replaced by all of the arguments that match the ellipsis, including commas between them.

How do you declare a Variadic function in C++?

Variadic functions are functions (e.g. std::printf) which take a variable number of arguments. To declare a variadic function, an ellipsis appears after the list of parameters, e.g. int printf(const char* format...);, which may be preceded by an optional comma.

Where is __ Va_args __ defined?

The C standard mandates that the only place the identifier __VA_ARGS__ can appear is in the replacement list of a variadic macro. It may not be used as a macro name, macro argument name, or within a different type of macro. It may also be forbidden in open text; the standard is ambiguous.

What are rust macros?

The term macro refers to a family of features in Rust: declarative macros with macro_rules! and three kinds of procedural macros: Custom #[derive] macros that specify code added with the derive attribute used on structs and enums. Attribute-like macros that define custom attributes usable on any item.


1 Answers

Expanding on the error: you get down to the case where there is only one value, and so it writes make_list!(1). However, there is no rule that will match that, for the second rule, after consuming the expression x, wants a comma, which is not provided.

So you need to make it so that it will work for make_list!(1) and not just (in fact, just not) make_list!(1,). To achieve this, get the comma inside the repeating part, like this:

macro_rules! make_list(
    () => (
        None
    );
    ( $x:expr $( , $more:expr )* ) => (
        Node::new($x, make_list!( $( $more ),* ))
    )
);

Bonus: you can write make_list![1, 2, 3] instead of make_list!(1, 2, 3) if you want.

like image 124
Chris Morgan Avatar answered Oct 01 '22 12:10

Chris Morgan