【问题标题】:Writing a macro that contains a match body编写包含匹配体的宏
【发布时间】:2014-12-06 08:07:40
【问题描述】:

我正在尝试压缩一些重复代码,其结构类似于:

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 可以是表达式或块(并且它也不处理可选地省略最后一个臂的逗号)。据我所知,没有办法指定宏模式的一部分可以是块或表达式,所以我想不出解决这个问题的方法。

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

您将如何编写这个宏?不写编译器插件也可以吗?

【问题讨论】:

    标签: macros pattern-matching rust


    【解决方案1】:

    我不知道你为什么决定这样做

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

    在 Rust 中,匹配臂始终是表达式,只是碰巧块也是表达式。

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

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

    进入这个:

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

    它的用法也应该改变:

                 $(
                     $pat => $result,
                 )*
    

                 $(
                     $pat => $result
                 ),*
    

    第二个问题是,除非您在包含 self 标识符的范围内(即在方法内部)定义此宏,否则由于卫生原因,它不会像您预期的那样工作 - 您在 @ 中使用的 self 标识符宏主体中的 987654328@ 和 self.bar() 调用将与宏扩展站点中的调用不同。作为一般规则,您始终需要将要在宏扩展站点上使用的标识符作为参数传递给宏,除非此宏是在这些标识符已经存在的范围内定义的。

    所以,宏的最终变体是这样的:

    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

    【讨论】:

    • 嗯,我想我误解了我收到的错误消息 - 它似乎无法处理块。请注意,此版本不允许您包含尾随逗号,因此我将$(,)* 放在模式的末尾。干杯!
    猜你喜欢
    • 2011-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-07
    相关资源
    最近更新 更多