Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to match methods in macros?

Tags:

The item fragment can match functions, but if the first argument of the function is a variant of self, i.e. if it is a method, it is not recognized as an item:

macro_rules! test {
    ( $fn:item ) => {}
}

// Ok
test! {
    fn foo() -> bool {
        true
    }
}

// Not ok
test! {
    fn foo(self) -> bool {
        true
    }
}

fn main() {}

How to match a method?

like image 579
Boiethios Avatar asked Jul 28 '18 13:07

Boiethios


1 Answers

A function that takes self is not an item, because it can't exist at the top level without an impl block to give a type to self.

The surrounding impl block is an item though.

To match the function, you'd have to break it down, perhaps like this:

macro_rules! test {
    ( fn $fn:ident ( self ) -> $ret:ty $body:block ) => {};
}

But you have to use the macro inside the impl block:

impl Foo {
    test! {
        fn foo(self) -> bool {
            true
        }
    }
}

You'll have to also handle quite a few permutations of possible kinds of function here though, which could end up getting quite repetitive:

// enables the ? operator for optional parts
#![feature(macro_at_most_once_rep)]

macro_rules! test {
    ( fn $fn:ident ( $($name:ident : $type:ty),* ) $( -> $ret:ty )? 
        $body:block 
    ) => {};
    ( fn $fn:ident ( self $(, $name:ident : $type:ty)* ) $( -> $ret:ty )? 
        $body:block 
    ) => {};
    ( fn $fn:ident ( &self $(, $name:ident : $type:ty)* ) $( -> $ret:ty )? 
        $body:block 
    ) => {};
    ( fn $fn:ident ( &mut self $(, $name:ident : $type:ty)* ) $( -> $ret:ty )? 
        $body:block
    ) => {};
}

And these don't even consider lifetime or type parameters.

Getting code reuse, by delegating these cases to another macro, might be tricky because you don't have the type of self inside the macro, so you might not be able to get rid of the repetition, unfortunately.

like image 80
Peter Hall Avatar answered Sep 28 '22 04:09

Peter Hall