Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unable to use self in macro because the macro expansion ignores token `self`

I want to write a macro that prints "OK" then returns self in a method. It's my first macro, so I tried this, thinking it will just make something like a text replacement, but it fails:

macro_rules! print_ok_and_return_self {
    () => {
        println!("OK");
        self
    }
}

fn main() {
    let a = A{};
    a.a().a();
}

struct A {}

impl A {
    fn a(self) -> Self {
        print_ok_and_return_self!()
    }
}

Error:

error: macro expansion ignores token `self` and any following
 --> src/main.rs:4:13
  |
4 |             self
  |             ^^^^
  |
note: caused by the macro expansion here; the usage of `print_ok_and_return_self!` is likely invalid in expression context
 --> src/main.rs:17:13
  |
17|             print_ok_and_return_self!()
  |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^

After a quick look at the documentation, I know it's not just text replacement, but I still don't know how to make it work.

like image 426
rap-2-h Avatar asked Nov 14 '16 16:11

rap-2-h


1 Answers

There are two errors in a row, let's fix the first one.

The syntax for a macro arm is:

(...) => {
    ...
}

which means that what your macro expands to is:

println!("OK");
self

which is not OK (two statements).

Instead, it should expand to an expression (in this case), which you get by enclosing it within {}:

macro_rules! print_ok_and_return_self {
    () => {
        {
            println!("OK");
            self
        }
    }
}

This leads to the second error:

error[E0424]: `self` is not available in a static method
  --> <anon>:4:9
   |
4  |         self
   |         ^^^^ not available in static method
...
17 |         print_ok_and_return_self!()
   |         --------------------------- in this macro invocation
   |
   = note: maybe a `self` argument is missing?

A macro cannot assume the existence of a variable in its scope, so you need to pass self as an argument:

macro_rules! print_ok_and_return_value {
    ($v:expr) => {{
        println!("OK");
        $v
    }}
}

and the invocation becomes:

impl A {
    fn a(self) -> Self {
        print_ok_and_return_value!(self)
    }
}
like image 146
Matthieu M. Avatar answered Sep 26 '22 21:09

Matthieu M.