Macros 写入包含匹配体的宏

Macros 写入包含匹配体的宏,macros,pattern-matching,rust,Macros,Pattern Matching,Rust,我试图浓缩一些重复代码,其结构类似于: match self.foo() { None => self.bar(), Some(MyStruct { foo: x, .. }) => match x { Pattern1 => result, Pattern2 => { block_result } } } 我想这样写: my_macro!( Pattern1 =&g

我试图浓缩一些重复代码,其结构类似于:

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

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


您将如何编写此宏?甚至不编写编译器插件也可以吗?

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

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

在Rust match中,手臂总是表达式,碰巧块也是表达式

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

$($pat:pat => $result:expr,)*
在这一点上:

$($pat:pat => $result:expr),*
其用途也应改变:

             $(
                 $pat => $result,
             )*

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

因此,宏的最后一个变体是:

macro_rules! my_macro (
    ($_self:expr, $($pat:pat => $result:expr),*) => (
        match $_self.foo() {
            None => $_self.bar(),
            Some(MyStruct { foo: x, .. }) => match x {
                 $(
                     $pat => $result
                 ),*
            },
        }
    );
)
它的工作原理完全符合你的要求


您可以找到有关宏以及如何编写宏的更多信息。

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

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

在Rust match中,手臂总是表达式,碰巧块也是表达式

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

$($pat:pat => $result:expr,)*
在这一点上:

$($pat:pat => $result:expr),*
其用途也应改变:

             $(
                 $pat => $result,
             )*

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

因此,宏的最后一个变体是:

macro_rules! my_macro (
    ($_self:expr, $($pat:pat => $result:expr),*) => (
        match $_self.foo() {
            None => $_self.bar(),
            Some(MyStruct { foo: x, .. }) => match x {
                 $(
                     $pat => $result
                 ),*
            },
        }
    );
)
它的工作原理完全符合你的要求


您可以找到有关宏以及如何编写宏的更多信息。

嗯,我想我误解了收到的错误消息-它似乎无法处理块。需要注意的是,这个版本不允许包含尾随逗号,因此我将
$(,)*
放在模式的末尾。干杯嗯,我猜我误解了我收到的错误消息-它似乎无法处理块。需要注意的是,这个版本不允许包含尾随逗号,因此我将
$(,)*
放在模式的末尾。干杯