[−][src]Crate auto_ops
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, import the macros with use auto_ops::*;
. Remember that you can only overload operators between one or more types defined in the current crate (respecting Rust orphan rules).
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 use auto_ops::impl_op; 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 use auto_ops::impl_op; 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 use auto_ops::impl_op; 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
Macros
impl_op | Overloads an operator using the given closure as its body. |
impl_op_commutative | Overloads a binary operator commutatively using the given closure as its body. |
impl_op_ex | Overloads an operator using the given closure as its body. Generates overloads for both owned and borrowed variants where possible. |
impl_op_ex_commutative | Overloads a binary operator commutatively using the given closure as its body. Generates overloads for both owned and borrowed variants where possible. |