Rust macros are not just text replacement. Instead, there are a couple of important differences, one of which is "macro hygiene". With this, identifiers inside the macro don't interfere with identifiers outside which does prevent a couple of bugs that commonly happen with macro systems like C's.
As a consequence, a macro can only access identifiers that are:
- passed explicitly, or
- are in scope when the macro is defined.
While it might seem like an unnecessary restriction at first, it actually helps with the readability of code. Otherwise, it's "spooky action at a distance". It's more or less the same reasoning why passing references to a variable into a function is done via some_fn(&mut foo)
in Rust and is not implicit like in C++ (some_fn(foo)
): it's clearer how a variable is used by a function at the call site.
This means we have two ways to fix your problem. The standard solution is to pass in self
to the macro:
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);
}
}
If you only need to use the macro inside the test
method, you can define the macro inside that method. Then, self
is already in scope when the macro is defined, so it works without passing self
:
struct F;
impl F {
fn dummy(&self) {}
fn test(&self) {
macro_rules! call_on_self {
($F:ident) => {
self.$F()
};
}
call_on_self!(dummy);
}
}
There's also fun combinations of the two, like defining a macro that takes self
explicitly and another macro defined inside the function to capture 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);
}
}
`