macro_rules! call_on_self {
($F:ident) => {
self.$F()
}
}
struct F;
impl F {
fn dummy(&self) {}
fn test(&self) {
call_on_self!(dummy);
}
}
The above doesn't work (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
I don't understand why this is not working: the macro is invoked in the method where self
is available! Is this somehow possible? Should I pass self
into the macro because otherwise the macro cannot resolve self
?
I'm using rustc 1.19.0-nightly.
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:
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);
}
}
`
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With