macro_rules! newtype_ops {
($($rest:tt)*) => { ... };
}Expand description
A macro for mass-forwarding operator impls on newtypes.
Some examples:
use newtype_ops::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! { [Foo] add : ^Self ^Self } // impl Add<Foo> for Foo { ... }
newtype_ops! { [Foo] add : ^Self ^i32 } // impl Add<i32> for Foo { ... }
newtype_ops! { [Foo] add : ^Self &Self } // impl Add<&Foo> for Foo { ... }
newtype_ops! { [Foo] add : ^Self &i32 } // impl Add<&i32> for Foo { ... }
newtype_ops! { [Foo] add : &Self ^Self } // impl Add<Foo> for &Foo { ... }
newtype_ops! { [Foo] add : &Self ^i32 } // impl Add<i32> for &Foo { ... }
newtype_ops! { [Foo] add : &Self &Self } // impl Add<&Foo> for &Foo { ... }
newtype_ops! { [Foo] add : &Self &i32 } // impl Add<&i32> for &Foo { ... }
newtype_ops! { [Foo] add = ^Self ^Self } // impl AddAssign<Foo> for Foo { ... }
newtype_ops! { [Foo] add = ^Self ^i32 } // impl AddAssign<i32> for Foo { ... }
newtype_ops! { [Foo] add = ^Self &Self } // Silently ignored [b]
newtype_ops! { [Foo] add = ^Self &i32 } // Silently ignored [b]
newtype_ops! { [Foo] add = &Self ^Self } // Silently ignored [a]
newtype_ops! { [Foo] add = &Self ^i32 } // Silently ignored [a]
newtype_ops! { [Foo] add = &Self &Self } // Silently ignored [a]
newtype_ops! { [Foo] add = &Self &i32 } // Silently ignored [a]
// ... Sub impls ...
// ... Mul impls ...
// ... Div impls ...
// ... Rem impls ...
newtype_ops! { [Foo] neg : ^Self ^Self } // impl Neg for Foo { ... }
newtype_ops! { [Foo] neg : ^Self ^i32 } // Silently ignored [c]
newtype_ops! { [Foo] neg : ^Self &Self } // Silently ignored [c]
newtype_ops! { [Foo] neg : ^Self &i32 } // Silently ignored [c]
newtype_ops! { [Foo] neg : &Self ^Self } // impl Neg for &Foo { ... }
newtype_ops! { [Foo] neg : &Self ^i32 } // Silently ignored [c]
newtype_ops! { [Foo] neg : &Self &Self } // Silently ignored [c]
newtype_ops! { [Foo] neg : &Self &i32 } // Silently ignored [c]
newtype_ops! { [Foo] neg = ^Self ^Self } // Silently ignored [a]
newtype_ops! { [Foo] neg = ^Self ^i32 } // Silently ignored [a]
newtype_ops! { [Foo] neg = ^Self &Self } // Silently ignored [a]
newtype_ops! { [Foo] neg = ^Self &i32 } // Silently ignored [a]
newtype_ops! { [Foo] neg = &Self ^Self } // Silently ignored [a]
newtype_ops! { [Foo] neg = &Self ^i32 } // Silently ignored [a]
newtype_ops! { [Foo] neg = &Self &Self } // Silently ignored [a]
newtype_ops! { [Foo] 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 missingXyzAssignimpls for borrowed args. Simply put, I forbade these so that the invocation at the top of the page works.[c]: Shortcomings in implementation.
ObviouslyNegdoesn’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 four words which stand in for groups of operators:
arithmeticis equivalent to{add sub mul div rem neg}arith_ringis equivalent to{add sub mul neg}bitwiseis equivalent to{bitand bitor bitxor not}integeris equivalent to{arithmetic bitwise}
There are also 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.
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. E.g. this is valid:
pub mod foo {
pub 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.