如何使用可变参数宏来调用嵌套构造函数?

时间:2014-07-01 14:08:12

标签: macros rust

我正在尝试在Rust中创建一个允许我编写

的宏
make_list!(1, 2, 3)

而不是

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

应该适用于任意数量的“参数”,包括零。这就是我到目前为止所做的:

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

但是我收到以下错误:

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

我无法理解这一点。从我所知,它应该工作。我做错了什么?

完整的代码:

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);
}

2 个答案:

答案 0 :(得分:8)

扩展错误:你只能得到只有一个值的情况,因此会写make_list!(1)。但是,没有规则可以匹配,对于第二条规则,在使用表达式x之后,需要一个未提供的逗号。

所以你需要做到这一点,以便它适用于make_list!(1)而不仅仅是(事实上,只是不是make_list!(1,)。要实现此目的,请在重复部分内部使用逗号,如下所示:

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

额外奖励:如果您愿意,可以写make_list![1, 2, 3]代替make_list!(1, 2, 3)

答案 1 :(得分:0)

正如@ chris-morgan的回答所指出的那样,扩大单一论证案例并未得到解决。

因此,您可以在扩展中包含逗号,或在宏中添加单个案例:

两个例子,单个参数:

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

在扩展中包含逗号:

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

以下是基于问题的完整工作示例,并针对Rust 1.14进行了更新:

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

#[derive(Debug)]
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<i64> = make_list!();
    println!("{:?}", list);
    let list: List<i64> = make_list!(1);
    println!("{:?}", list);
    let list: List<i64> = make_list!(1,2,3,4,5,6,7,8,9);
    println!("{:?}", list);
}