如何在宏中调用 self 的方法?

24
macro_rules! call_on_self {
    ($F:ident) => {
        self.$F()
    }
}

struct F;
impl F {
    fn dummy(&self) {}
    fn test(&self) {
        call_on_self!(dummy);
    }
}

上述方案无法运行 (Playground):

error[E0424]: expected value, found module `self`
  --> src/lib.rs:3:9
   |
3  |         self.$F()
   |         ^^^^ `self` value is a keyword only available in methods with `self` parameter
...
11 |         call_on_self!(dummy);
   |         --------------------- in this macro invocation

我不明白为什么这个不能工作:宏是在有self的方法中调用的!这可能怎么可能?我是否应该将self传递给宏,否则宏将无法解析self

我正在使用rustc 1.19.0-nightly。


1
如果gen_match仅被test使用,您可以将macro_rules!移动到函数内部。 - kennytm
1个回答

42

Rust宏不仅是文本替换,其与其他宏系统(如C语言)相比有几个重要区别之一是“宏卫生”。这意味着宏内的标识符不会干扰外部标识符,从而避免了一些常见的宏系统错误。

因此,在宏中只能访问以下标识符:

  • 显式传递的标识符,或者
  • 在宏定义时处于作用域中的标识符。

虽然乍一看似乎是不必要的限制,但实际上有助于代码可读性。否则,就会出现“幽灵作用”。这与将变量的引用传递到函数中通过 some_fn(&mut foo) 在Rust中进行而不是像C++那样隐含地进行(some_fn(foo))的原因几乎相同:这更清楚地说明了变量在调用函数时的使用方式。

这意味着我们有两种方法来解决您的问题。标准解决方案是self传递给宏

macro_rules! call_on_self {
    ($self:ident, $F:ident) => {
        $self.$F()
    };
}

struct F;
impl F {
    fn dummy(&self) {}
    fn test(&self) {
        call_on_self!(self, dummy);
    }
}

如果您只需要在test方法内使用宏,您可以在该方法内定义宏。然后,在定义宏时,self已经在作用域内,因此无需传递self

如果您只需要在test方法内使用宏,您可以在该方法内定义宏。然后,在定义宏时,self已经在作用域内,因此无需传递self

struct F;
impl F {
    fn dummy(&self) {}
    fn test(&self) {
        macro_rules! call_on_self {
            ($F:ident) => {
                self.$F()
            };
        }

        call_on_self!(dummy);
    }
}

还有一些有趣的组合方式,比如定义一个显式接受self参数的宏,以及在函数内部定义另一个宏来捕获self:

macro_rules! call_on_self_outer {
    ($self:ident, $F:ident) => {
        $self.$F()
    };
}

struct F;
impl F {
    fn dummy(&self) {}
    fn test(&self) {
        macro_rules! call_on_self {
            ($F:ident) => {
                call_on_self_outer!(self, $F);
            };
        }

        call_on_self!(dummy);
    }
}
`

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