Expand description
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.