Macro newtype_ops::newtype_ops [] [src]

macro_rules! newtype_ops {
    ($($rest:tt)*) => { ... };
}

A macro for mass-forwarding operator impls on newtypes.

#[macro_use]
extern crate newtype_ops;

// derive everything under the sun
pub struct Foo(pub i32);
newtype_ops! { [Foo] integer {:=} {^&}Self {^&}{Self i32} }

// derive everything under the sun for floats
pub struct Bar(pub f32);
newtype_ops! { [Bar] arithmetic {:=} {^&}Self {^&}{Self f32} }

These two impls are equivalent to this:

newtype_ops! { [Foo] {add sub mul div rem neg
                      not bitand bitor bitxor} {:=} {^&}Self {^&}{Self i32} }
newtype_ops! { [Bar] {add sub mul div rem neg} {:=} {^&}Self {^&}{Self f32} }

Which are in turn equivalent to this; every braced portion is expanded in a cartesian product of the tokens inside. To give you a feel for what the various parts of the syntax stand for, the impls are labeled in comments.

newtype_ops! { [Bar] add : ^Self ^Self } // impl Add<Bar>  for Bar { ... }
newtype_ops! { [Bar] add : ^Self ^i32  } // impl Add<i32>  for Bar { ... }
newtype_ops! { [Bar] add : ^Self &Self } // impl Add<&Bar> for Bar { ... }
newtype_ops! { [Bar] add : ^Self &i32  } // impl Add<&i32> for Bar { ... }
newtype_ops! { [Bar] add : &Self ^Self } // impl Add<Bar>  for &Bar { ... }
newtype_ops! { [Bar] add : &Self ^i32  } // impl Add<i32>  for &Bar { ... }
newtype_ops! { [Bar] add : &Self &Self } // impl Add<&Bar> for &Bar { ... }
newtype_ops! { [Bar] add : &Self &i32  } // impl Add<&i32> for &Bar { ... }
newtype_ops! { [Bar] add = ^Self ^Self } // impl AddAssign<Bar> for Bar { ... }
newtype_ops! { [Bar] add = ^Self ^i32  } // impl AddAssign<i32> for Bar { ... }
newtype_ops! { [Bar] add = ^Self &Self } // Silently ignored [b]
newtype_ops! { [Bar] add = ^Self &i32  } // Silently ignored [b]
newtype_ops! { [Bar] add = &Self ^Self } // Silently ignored [a]
newtype_ops! { [Bar] add = &Self ^i32  } // Silently ignored [a]
newtype_ops! { [Bar] add = &Self &Self } // Silently ignored [a]
newtype_ops! { [Bar] add = &Self &i32  } // Silently ignored [a]
// ... Sub impls ...
// ... Mul impls ...
// ... Div impls ...
// ... Rem impls ...
newtype_ops! { [Bar] neg : ^Self ^Self } // impl Neg for Bar { ... }
newtype_ops! { [Bar] neg : ^Self ^i32  } // Silently ignored [c]
newtype_ops! { [Bar] neg : ^Self &Self } // Silently ignored [c]
newtype_ops! { [Bar] neg : ^Self &i32  } // Silently ignored [c]
newtype_ops! { [Bar] neg : &Self ^Self } // impl Neg for &Bar { ... }
newtype_ops! { [Bar] neg : &Self ^i32  } // Silently ignored [c]
newtype_ops! { [Bar] neg : &Self &Self } // Silently ignored [c]
newtype_ops! { [Bar] neg : &Self &i32  } // Silently ignored [c]
newtype_ops! { [Bar] neg = ^Self ^Self } // Silently ignored [a]
newtype_ops! { [Bar] neg = ^Self ^i32  } // Silently ignored [a]
newtype_ops! { [Bar] neg = ^Self &Self } // Silently ignored [a]
newtype_ops! { [Bar] neg = ^Self &i32  } // Silently ignored [a]
newtype_ops! { [Bar] neg = &Self ^Self } // Silently ignored [a]
newtype_ops! { [Bar] neg = &Self ^i32  } // Silently ignored [a]
newtype_ops! { [Bar] neg = &Self &Self } // Silently ignored [a]
newtype_ops! { [Bar] neg = &Self &i32  } // Silently ignored [a]

Holy blazing swordfish, you should reply, why are so many impls silently ignored?

Well, a couple of reasons. They're labeled:

  • [a]: They're nonsense/impossible, yet cartesian products generate them.
  • [b]: For not very good reasons.
    AddAssign<&Bar> and AddAssign<&i32> are certainly possible. However, the majority of newtype wrappers are likely around primitives, and primitive types are mysteriously missing XyzAssign impls for borrowed args. Simply put, I forbade these so that the invocation at the top of the page works.
  • [c]: Shortcomings in implementation.
    Obviously Neg doesn't care about an argument type, but gets paired with a whole bunch of them anyways thanks to the cartesian product design. The labeled invocations are thus ignored to avoid generating multiple conflicting impls. ## Other notes:

There are word equivalents for : (normal) and = (assign).

There are deliberately NOT operator shortcuts such as + for add, because it is too easy to accidentally embed a comment delimiter (+-*/%).

The caret ^ before value types can be omitted. Its raison d'être is to give you something to put in a cartesian product, as {^&}.

newtype_ops! { [Bar] add : Self Self }

You can omit the final type, in which case it is inferred to be Self. This is for the sake of unary ops, but it currently also affects others...

newtype_ops! { [Bar] neg : Self } // Allowed
newtype_ops! { [Bar] add : Self } // Also allowed (but discouraged; may change)

The implemented type is bracketed (a) to help parse it, and (b) so that it can use a cartesian product, which currently operates specifically on token trees. This is valid:

pub mod foo {
    struct Foo(pub i32);
}
pub struct Bar(pub i32);
pub struct Generic<T>(T);

newtype_ops! { {[foo::Foo] [Bar] [Generic<i32>]} arithmetic {:=} Self Self }

The output type can also be anything arbitrary like foo::Foo or Generic<i32>, so long as self.0 (&self.0 for &Self receivers) implements Add for that type. However, be careful with cartesian products; {Self i32} only works because Self and i32 are individual token trees. {foo::Foo Generic<i32>} will fail miserably.

The Index, Deref, and Fn trait hierarchies are considered in-scope for future additions, but hell if I know how to fit them into the existing interface.