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>
andAddAssign<&i32>
are certainly possible. However, the majority of newtype wrappers are likely around primitives, and primitive types are mysteriously missingXyzAssign
impls for borrowed args. Simply put, I forbade these so that the invocation at the top of the page works.[c]
: Shortcomings in implementation.
ObviouslyNeg
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.