编写包含匹配正文的宏 [英] Writing a macro that contains a match body

查看:69
本文介绍了编写包含匹配正文的宏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试压缩一些结构类似于以下的重复代码:

I'm attempting to condense some repetitive code that has structure similar to:

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销毁.

avoiding the repetitive None handling and MyStruct destructuring.

这似乎应该非常简单,因为它实际上只是将宏主体替换为match表达式,但是我实际上看不到任何方法.

This seems like it should be pretty simple, as it's essentially just substituting the macro body into a match expression, but I can't actually see any way to do this.

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

I attempted to write the macro as follows:

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

但这失败了,因为匹配臂的RHS可以是表达式或块(并且也不能选择省略最后一个臂的逗号).据我所知,无法指定宏模式的一部分可以是块或表达式,所以我想不出一种解决方法.

but this fails, as the RHS of a match arm can be an expression or a block (and it also doesn't deal with optionally omitting the comma for the last arm). As far as I'm aware there's no way to specify that part of a macro pattern can be a block or an expression, so I can't think of a way to resolve this.

理想情况下,我希望这样做,而不必编写复杂的模式来使匹配体解构,只是将它们重新粘在一起,但我认为没有任何指定者会接受匹配表达式的体.

Ideally I'd like to do this without having to write complicated patterns that destructure match bodies only to stick them back together again, but I don't think there's any designator that will accept the body of a match expression.

您将如何编写此宏?如果不编写编译器插件,是否有可能?

How would you go about writing this macro? Is it even possible without writing a compiler plugin?

推荐答案

我不确定您为什么决定

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

this fails, as the RHS of a match arm can be an expression or a block

在Rust比赛中,手臂始终是表达式,而碰巧块也是表达式.

In Rust match arms are always expressions, and it just happened that blocks are also expressions.

您的宏中有两个问题.首先,您注意到,您的宏无法处理省略最后一个逗号.这很容易补救:您只需更改此模式:

There are two problems in your macro. First, as you noticed, your macro does not handle omitting last comma. This can be remedied very easily: you just change this pattern:

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

进入这一个:

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

及其用法也应更改:

             $(
                 $pat => $result,
             )*

             $(
                 $pat => $result
             ),*

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

The second problem is that unless you define this macro in the scope which contains self identifier (i.e. inside the method), it won't work as you expect because of hygiene - the self identifier you use in self.foo() and self.bar() invocations in the macro body will be different from the one at the macro expansion site. As a general rule, you always need to pass identifiers you want to work with at the macro expansion site as arguments to the macro, unless this macro is defined in the scope where these identifiers are already present.

因此,宏的最终变体是这样:

So, the final variant of the macro is this:

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

它确实可以按照您的要求工作.

and it does work exactly as you want.

您可以在官方指南中找到有关宏以及如何编写宏的更多信息 .

You can find more information on macros and how to write them in the official guide.

这篇关于编写包含匹配正文的宏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆