Background: downcast-rs takes a trait and defines methods on it for downcasting.
You don’t have to what this crate does. My question is entirely about making the macro DRYer than writing out an exponential explosion of patterns.
Problem: I recently needed to add support for traits with associated types and did a significant refactor to make things DRYer in preparation for it. Now this macro now supports downcasting for various combinations of the existence or non-existence of (1) type parameters, (2) associated types, (3) type constraints (where clauses), and (4) whether the types in the traits are generic or concrete.
Ignoring the concrete type case and the case of a Trait with no parameters or associated types, this macro supports following forms:
impl_downcast!(Trait<T1, T2>); // with type parameters impl_downcast!(Trait assoc AT1, AT2); // with associated types impl_downcast!(Trait<T1, T2> assoc AT1, AT2); // with type parameters an associated types impl_downcast!(Trait<T1, T2> where T1: Copy); // with type constraints // ... and the other variations with `where` clauses.
These can be summed up via the following macro pattern, using
$(...)? to mean optional occurrence:
impl_downcast!(Trait $( < $($type:ident),* > )? $( assoc $($atype:ident),* )? $( where $($pred:tt)* )?
But there seems to not be an elegant way to capture this optional-ness.
- Pattern matching seems to require dealing with the full exponential explosion of possibilities, at least while keeping this macro readable (i.e. avoiding incremental TT munching).
- The helper patterns dealing with the variations of the
whereclause are similarly not very DRY.
If I had to add yet another orthogonal feature, I’d have to double the number of patterns – one for those with the feature, and one for those without rather than simply indicating it as an optional part of the pattern.
Is there some clean, elegant, and readable way to do a functional
if within a macro to avoid such explosions?