Crate overload

source ·
Expand description

Provides a macro to simplify operator overloading.

To use, include the following:

extern crate overload;
use overload::overload;
use std::ops; // <- don't forget this or you'll get nasty errors

Introduction

Suppose we have the following struct definition:

#[derive(PartialEq, Debug)]
struct Val {
    v: i32
}

We can overload the addition of Vals like so:

overload!((a: Val) + (b: Val) -> Val { Val { v: a.v + b.v } });

The macro call above generates the following code:

impl ops::Add<Val> for Val {
    type Output = Val;
    fn add(self, b: Val) -> Self::Output {
        let a = self;
        Val { v: a.v + b.v }
    }
}

We are now able to add Vals:

assert_eq!(Val{v:3} + Val{v:5}, Val{v:8});

Owned and borrowed types

If we also wanted to overload addition for the borrowed type &Val we could write:

overload!((a: &Val) + (b: &Val) -> Val { Val { v: a.v + b.v } });

We might also want to overload addition between the owned and borrowed types:

overload!((a: Val) + (b: &Val) -> Val { Val { v: a.v + b.v } });
overload!((a: &Val) + (b: Val) -> Val { Val { v: a.v + b.v } });

Let’s see how we can write these combinations more concisely.

We can include a ? in front of a type to indicate that it should stand in for both the owned and borrowed type.

To overload addition for all four combinations between Val and &Val we can therefore simply include a ? in front of both types:

overload!((a: ?Val) + (b: ?Val) -> Val { Val { v: a.v + b.v } });

The macro call above generates the following code:

impl ops::Add<Val> for Val {
    type Output = Val;
    fn add(self, b: Val) -> Self::Output {
        let a = self;
        Val { v: a.v + b.v }
    }
}
 
impl ops::Add<&Val> for Val {
    type Output = Val;
    fn add(self, b: &Val) -> Self::Output {
        let a = self;
        Val { v: a.v + b.v }
    }
}
 
impl ops::Add<Val> for &Val {
    type Output = Val;
    fn add(self, b: Val) -> Self::Output {
        let a = self;
        Val { v: a.v + b.v }
    }
}
 
impl ops::Add<&Val> for &Val {
    type Output = Val;
    fn add(self, b: &Val) -> Self::Output {
        let a = self;
        Val { v: a.v + b.v }
    }
}

We are now able to add Vals and &Vals in any combination:

assert_eq!(Val{v:3} + Val{v:5}, Val{v:8});
assert_eq!(Val{v:3} + &Val{v:5}, Val{v:8});
assert_eq!(&Val{v:3} + Val{v:5}, Val{v:8});
assert_eq!(&Val{v:3} + &Val{v:5}, Val{v:8});

Binary operators

The general syntax to overload a binary operator between types <a_type> and <b_type> is:

overload!((<a_ident>: <a_type>) <op> (<b_ident>: <b_type>) -> <out_type> { /*body*/ });

Inside the body you can use <a_ident> and <b_ident> freely to perform any computation.

The last line of the body needs to be an expression (i.e. no ; at the end of the line) of type <out_type>.

OperatorExampleTrait
+overload!((a: A) + (b: B) -> C { /*...*/ );Add
-overload!((a: A) - (b: B) -> C { /*...*/ );Sub
*overload!((a: A) * (b: B) -> C { /*...*/ );Mul
/overload!((a: A) / (b: B) -> C { /*...*/ );Div
%overload!((a: A) % (b: B) -> C { /*...*/ );Rem
&overload!((a: A) & (b: B) -> C { /*...*/ );BitAnd
|overload!((a: A) | (b: B) -> C { /*…*/ );BitOr
^overload!((a: A) ^ (b: B) -> C { /*...*/ );BitXor
<<overload!((a: A) << (b: B) -> C { /*...*/ );Shl
>>overload!((a: A) >> (b: B) -> C { /*...*/ );Shr

Assignment operators

The general syntax to overload an assignment operator between types <a_type> and <b_type> is:

overload!((<a_ident>: &mut <a_type>) <op> (<b_ident>: <b_type>) { /*body*/ });

Inside the body you can use <a_ident> and <b_ident> freely to perform any computation and mutate <a_ident> as desired.

OperatorExampleTrait
+=overload!((a: &mut A) += (b: B) { /*...*/ );AddAssign
-=overload!((a: &mut A) -= (b: B) { /*...*/ );SubAssign
*=overload!((a: &mut A) *= (b: B) { /*...*/ );MulAssign
/=overload!((a: &mut A) /= (b: B) { /*...*/ );DivAssign
%=overload!((a: &mut A) %= (b: B) { /*...*/ );RemAssign
&=overload!((a: &mut A) &= (b: B) { /*...*/ );BitAndAssign
|=overload!((a: &mut A) |= (b: B) { /*…*/ );BitOrAssign
^=overload!((a: &mut A) ^= (b: B) { /*...*/ );BitXorAssign
<<=overload!((a: &mut A) <<= (b: B) { /*...*/ );ShlAssign
>>=overload!((a: &mut A) >>= (b: B) { /*...*/ );ShrAssign

Unary operators

The general syntax to overload a unary operator for type <a_type> is:

overload!(<op> (<a_ident>: <a_type>) -> <out_type> { /*body*/ });

Inside the body you can use <a_ident> freely to perform any computation.

The last line of the body needs to be an expression (i.e. no ; at the end of the line) of type <out_type>.

OperatorExampleTrait
-overload!(- (a: A) -> B { /*...*/ );Neg
!overload!(! (a: A) -> B { /*...*/ );Not

Notes

Remember that you can only overload operators between one or more types if at least one of the types is defined in the current crate.

Macros