So I took my own stab at a derivation macro which tries to compress all or most of the derivations somebody may need into one.
As it turns out, there are problems. (surprise!)
1. There are many types of impls you may want to derive.
pub struct Foo(i32);
// the plainest choice...
impl Add<Foo> for Foo { ... }
// but you might want these as well...
impl<'a> Add<&'a Foo> for Foo { ... }
impl<'b> Add<Foo> for &'b Foo { ... }
impl<'a,'b> Add<&'a Foo> for &'b Foo { ... }
// In some cases, maybe you'll want these, too. I won't judge:
impl Add<i32> for Foo { ... }
impl<'a> Add<&'a i32> for Foo { ... }
impl<'b> Add<i32> for &'b Foo { ... }
impl<'a,'b> Add<&'a i32> for &'b Foo { ... }
// Aw man, don't forget the assign traits!
impl AddAssign<Foo> for Foo { ... }
impl AddAssign<&Foo> for Foo { ... } // NOTE: see point 2
impl AddAssign<i32> for Foo { ... }
impl AddAssign<&i32> for Foo { ... } // NOTE: see point 2
You see how the number of options explodes with each thing we add. Generally, speaking, I think what we want is often some sort of direct product of possibilities, and we would want a derivation macro to be able to provide all of these dimensions.
2. We must avoid generating invalid impls.
Two of the impls above have been specially marked with a note. These impls don't actually exist on primitive numeric types. Phooey, huh? (in general, an impl will require Clone)
In general, invalid impls are a big problem because we have to avoid generating them. And we can't just tell a macro to generate "whatever works;" there's no way for it to know what does! Unfortunately, where clauses cannot provide us with an easy way out, because bounds on fully monomorphized types are checked at compile time:
pub struct Bar(i32);
pub struct Foo(Bar);
use ::std::ops::Add;
// compiler says "not a chance."
// error[E0277]: the trait bound `Bar: std::ops::Add` is not satisfied
impl Add for Foo where Bar: Add<Bar,Output=Bar> { }
3. The Rust macro system is... the Rust macro system.
You know that feeling when nothing at all in the final product looks anything at all like your original design? macro_rules!
is the pure distilled essence of that feeling 24/7, no refunds.
So yeah. I wrote a macro which does "x"... but look at it even slightly funny and the facade shatters completely. Also, as an implementation detail, it tickle tortures kittens on each compile. That said, it seems to work alright...
Anyways, it's called newtype-ops and it's the first crate I've actually bothered publishing (not because I'm at all confident in its implementation, but rather to take advantage of docs.rs). I've already switched one small project over to using it, which now implements all of its operators in a single line:
newtype_ops!{ {[Frac][Cart]} arithmetic {:=} {^&}Self {^&}Self }
Aside from the many documented flaws, I'd like to know what use cases people have for which the macro doesn't suffice. Understanding the problem space is a necessary part of creating a better solution, and I feel as though I significantly underestimated the scope of the problem initially...