编写包含匹配体的宏

时间:2014-12-06 08:07:40

标签: macros pattern-matching rust

我试图压缩一些结构类似于:

的重复代码
match self.foo() {
    None => self.bar(),
    Some(MyStruct { foo: x, .. }) => match x {
        Pattern1 => result,
        Pattern2 => {
            block_result
        }
    }
}

我想写的是:

my_macro!(
    Pattern1 => result,
    Pattern2 => {
        block_result
    }
)

避免重复的None处理和MyStruct解构。

这看起来应该很简单,因为它基本上只是将宏体代入匹配表达式,但我实际上看不到任何方法来做到这一点。

我尝试按如下方式编写宏:

macro_rules! my_macro (
    ($($pat:pat => $result:expr,)*) => (
        match self.foo() {
            None => self.bar(),
            Some(MyStruct { foo: x, .. }) => match x {
                 $(
                     $pat => $result,
                 )*
            },
        }
    );
)

但是失败了,因为匹配臂的RHS可以是表达式或块(并且它也不处理可选地省略最后一个臂的逗号)。据我所知,没有办法指定宏模式的一部分可以是块或表达式,所以我想不出解决这个问题的方法。

理想情况下,我想这样做而不必编写复杂的模式来解构匹配的主体只是为了再次将它们重新组合在一起,但我认为没有任何指示符可以接受匹配表达式的主体。

你会如何写这个宏?如果不编写编译器插件,它甚至可以吗?

1 个答案:

答案 0 :(得分:5)

我不确定你为什么决定

  

这失败了,因为匹配臂的RHS可以是表达式或块

在Rust匹配中,arm总是表达式,而恰好是块也是表达式。

您的宏有两个问题。首先,正如您所注意到的,您的宏不会处理省略最后一个逗号。这可以很容易地解决:您只需更改此模式:

$($pat:pat => $result:expr,)*

进入这一个:

$($pat:pat => $result:expr),*

及其用法也应该改变:

             $(
                 $pat => $result,
             )*

             $(
                 $pat => $result
             ),*

第二个问题是,除非您在包含self标识符的范围内定义此宏(即在方法内部),否则由于卫生而无法按预期工作 - self标识符您在self.foo()中使用,并且宏体中的self.bar()调用将与宏扩展站点上的调用不同。作为一般规则,您始终需要将要在宏扩展站点上使用的标识符作为宏的参数传递,除非此宏在已存在这些标识符的范围内定义。 / p>

因此,宏的最终变体是:

macro_rules! my_macro (
    ($_self:expr, $($pat:pat => $result:expr),*) => (
        match $_self.foo() {
            None => $_self.bar(),
            Some(MyStruct { foo: x, .. }) => match x {
                 $(
                     $pat => $result
                 ),*
            },
        }
    );
)

它确实可以正常工作。

您可以找到有关宏的更多信息以及如何编写它们in the official guide