9
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。

4

1 回答 1

20

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

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);
    }
}
`
于 2017-05-23T01:41:06.597 回答