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

10

我正在尝试在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);
}

您的宏接受0或2个参数,但您只传递了1个。 - Arjan
1
@Arjan:但我认为这就是$(...),*的作用。它应该匹配零到多个参数。不是吗? - sellibitze
2个回答

10
扩展错误:当只有一个值时,将写入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)


谢谢!我想我只是太习惯于 C++ 可变参数模板的神奇了。 :) - sellibitze
2
这种方法已经不再适用了,请参见此处 - bgilbert
似乎又可以工作了。 - Chris Morgan

1

正如@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);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接