impl_ops 0.1.1

Macros for easy operator overloading.
Documentation

Macros for easy operator overloading.

The primary macro to learn is impl_op!(<op> <closure>); where <op> is an operator and <closure> is a closure with the same signature as the trait function associated with <op>. The macro you'll actually want to use most of the time, however, is impl_op_ex!. It works the same way as impl_op! but with some extra magic behind the scenes.

To use, include #[macro_use] extern crate impl_ops; in your crate and use std::ops; in your module. Remember that you can only overload operators between one or more types defined in the current crate.

Examples

All of the following examples are run with the following struct defined:

#[derive(Clone, Debug, PartialEq)]
struct DonkeyKong {
    pub bananas: i32,
}
impl DonkeyKong {
    pub fn new(bananas: i32) -> DonkeyKong {
        DonkeyKong { bananas: bananas }
    }
}

Binary operators

// impl_op!(op |a: LHS, b: RHS| -> OUT {...});
// impl_op!(op |a: LHS, b: &RHS| -> OUT {...});
// impl_op!(op |a: &LHS, b: RHS| -> OUT {...});
// impl_op!(op |a: &LHS, b: &RHS| -> OUT {...});
// where
// OP  : +, -, *, /, %, &, |, ^, <<, >>
// a, b: variable names

#[macro_use] extern crate impl_ops;
use std::ops;
# #[derive(Clone, Debug, PartialEq)]
# struct DonkeyKong {
#     pub bananas: i32,
# }
# impl DonkeyKong {
#     pub fn new(bananas: i32) -> DonkeyKong {
#         DonkeyKong { bananas: bananas }
#     }
# }

impl_op!(- |a: DonkeyKong, b: i32| -> DonkeyKong { DonkeyKong::new(a.bananas - b) });
impl_op!(+ |a: &DonkeyKong, b: &DonkeyKong| -> i32 { a.bananas + b.bananas });

fn main() {
    let dk = DonkeyKong::new(3) - 1;
    assert_eq!(DonkeyKong::new(2), dk);
    let total_bananas = &dk + &DonkeyKong::new(4);
    assert_eq!(6, total_bananas);
}

Assignment operators

// impl_op!(OP |a: &mut LHS, b: RHS| {...});
// impl_op!(op |a: &mut LHS, b: &RHS| {...})
// where
// op  : +=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=
// a, b: variable names

#[macro_use] extern crate impl_ops;
use std::ops;
# #[derive(Clone, Debug, PartialEq)]
# struct DonkeyKong {
#     pub bananas: i32,
# }
# impl DonkeyKong {
#     pub fn new(bananas: i32) -> DonkeyKong {
#         DonkeyKong { bananas: bananas }
#     }
# }

impl_op!(+= |a: &mut DonkeyKong, b: DonkeyKong| { a.bananas += b.bananas });
impl_op!(+= |a: &mut DonkeyKong, b: &DonkeyKong| { a.bananas += b.bananas });

fn main() {
    let mut dk = DonkeyKong::new(3);
    dk += DonkeyKong::new(1);
    assert_eq!(DonkeyKong::new(4), dk);
    dk += &DonkeyKong::new(1);
    assert_eq!(DonkeyKong::new(5), dk);
}

Unary operators

// impl_op!(OP |a: LHS| -> OUT {...});
// impl_op!(op |a: &LHS| -> OUT {...})
// where
// OP: !, -
// a : variable name

#[macro_use] extern crate impl_ops;
use std::ops;
# #[derive(Clone, Debug, PartialEq)]
# struct DonkeyKong {
#     pub bananas: i32,
# }
# impl DonkeyKong {
#     pub fn new(bananas: i32) -> DonkeyKong {
#         DonkeyKong { bananas: bananas }
#     }
# }

impl_op!(- |a: DonkeyKong| -> DonkeyKong { DonkeyKong::new(-a.bananas) });
impl_op!(- |a: &DonkeyKong| -> DonkeyKong { DonkeyKong::new(-a.bananas) });

fn main() {
    let dk = -DonkeyKong::new(3);
    assert_eq!(DonkeyKong::new(-3), dk);
    assert_eq!(DonkeyKong::new(3), -&dk);
}

Limitations

  • The output type of any operation must be an owned type (i.e. impl_op!(+ |a: DonkeyKong b: i32| -> &DonkeyKong {...}) is invalid).
  • Types that have an unqualified lifetime or associated type are invalid
// impl_op!(+ |a: SomeType<'a>, b: SomeType<'a>| -> SomeType<'a> {...}) // INVALID
// impl_op!(+ |a: SomeType<T>, b: SomeType<T>| -> SomeType<T> {...})    // INVALID
impl_op!(+ |a: SomeType<i32>, b: SomeType<i32>| -> SomeType<i32> {...}) // VALID