Macro that implements functions based on type

561 Views Asked by At

How can I write a macro that implements the same methods for multiple structs? The methods should be slightly different based on the struct.

The code should look something like this:

macro_rules! say_name {
    (for $($t:ty),+) => {
        $(impl $t {
            fn say_name(&self) {
                //if $t == A then self.say_a();
                //if $t == B then self.say_b();
            }
        })*
    };
}

struct A {

}

impl A {
    fn say_a(&self) {
        println!("A");
    }
}

struct B {

}

impl B {
    fn say_b(&self) {
        println!("B");
    }
}

say_name!(for A, B);

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

My code is a little bit complicated so I provided a dummy code for the purpose of the question.

2

There are 2 best solutions below

0
On

You can define a trait next to the macro definition that the macro definition can use, and which "specializes" for A or B:

pub mod private { // this mod has to be pub so the expanded code can use it
    pub trait SayNameImpl {
        fn say_name_impl(&self);
    }

    impl SayNameImpl for super::A {
        fn say_name_impl(&self) {
            self.say_a();
        }
    }

    impl SayNameImpl for super::B {
        fn say_name_impl(&self) {
            self.say_b();
        }
    }
}

macro_rules! say_name {
    (for $($t:ty),+) => {
        $(impl $t {
            fn say_name(&self) {
                use $crate::private::SayNameImpl;
                self.say_name_impl();
            }
        })*
    };
}

Playground

0
On

You can pass the method name to the macro like so

macro_rules! say_name {
    (for $($t:ty = ($f:ident) ),+) => {
        $(impl $t {
            fn say_name(&self) {
                <$t>::$f(self)
            }
        })*
    };
}

It is now called with

say_name!(for A = (say_a), B = (say_b));

playground