Creating a macro to reduce boilerplate for implementing operator traits

I'm attempting to implement a macro which will reduce the boilerplate involved with implementing the core operator traits (Add, Sub, Mul, Div, etc). However, I'm having a bit of trouble with the following section:

macro_rules! impl_operator {
    ($tr: ident, $method: ident) => {
        impl $tr<Prime> for Prime {
            type Output = Prime;
            #[inline]
            fn $method(self, rhs: Prime) -> Prime {
                Prime {
                    num: (self.num.$method(rhs.num))
                }
            }
        }

        impl<'b, 'a> $tr<&'b Prime> for &'a Prime {
            type Output = Prime;
            #[inline]
            fn $method(&'a self, rhs: &'b Prime) -> Prime {
                Prime {
                    num: (self.num.$method(rhs.num))
                }
            }
        }
    };
}

In the above example, the Prime type is a custom struct created in my program. The num member is another type that's defined in an external library. That type already implements the traits that I want; my macro will simply piggy-back off of those implementations.

When implementing an operator trait where the types involves are all references, I receive the following compiler error (when testing, I attempted to implement the Add trait):

prime.rs:160:27: 160:31 error: cannot move out of borrowed content [E0507]
prime.rs:160                     num: (self.num.$method(rhs.num))
                                       ^~~~
prime.rs:167:1: 167:26 note: in this expansion of impl_operator! (defined in prime.rs)
prime.rs:160:44: 160:47 error: cannot move out of borrowed content [E0507]
prime.rs:160                     num: (self.num.$method(rhs.num))
                                                        ^~~
prime.rs:167:1: 167:26 note: in this expansion of impl_operator! (defined in prime.rs)
error: aborting due to 2 previous errors

Of course, the issue here is caused by the borrow checker not liking me attempting to consume a reference to the inputs. How can I go about solving this?

This is a guess, but you should just be able to explicitly borrow the arguments to the operator:

use std::ops::Add;

struct Bjorn;

impl Add<Bjorn> for Bjorn {
    type Output = Bjorn;
    fn add(self, _: Self) -> Bjorn {
        Bjorn
    }
}

impl<'a> Add<&'a Bjorn> for &'a Bjorn {
    type Output = Bjorn;
    fn add(self, _: &'a Bjorn) -> Bjorn {
        Bjorn
    }
}

struct Spim(Bjorn);

impl<'a> Add<&'a Spim> for &'a Spim {
    type Output = Spim;
    fn add(self, rhs: &'a Spim) -> Spim {
        Spim((&self.0) + (&rhs.0))
    }
}

fn main() {}

Incidentally, I don't suppose you could just use newtype_derive?

1 Like

Even when explicitly borrowing the arguments to the operator, I'm still receiving the cannot move out of borrow content error.

Well, that code works for me, so without details of what you're doing differently, I don't know what else to suggest. Even if I use your macro directly (provided I fix the problems with it), it works fine.

macro_rules! impl_operator {
    ($tr: ident, $method: ident) => {
        // ...

        impl<'b, 'a> $tr<&'b Prime> for &'a Prime {
            type Output = Prime;
            #[inline]
            fn $method(self, rhs: &'b Prime) -> Prime {
                Prime {
                    num: ((&self.num).$method(&rhs.num))
                }
            }
        }
    };
}

Ahah! I feel silly now. I thought that I had tried surrounding the &self.num part in parens, but apparently I didn't. That works! Thank you for the help.