If your list of types is manageable, so if you don't need a blanket implementation, the easiest is to create them "by hand". You have 3 options for that:
- Really write the combinations by hand, but it's tedious and poorly maintainable.
- Use a declarative macro to repeat the implementation for a list of types; you'll find a lot of that in the standard library; for example, for operations supported all the integer types. The downside is very poor readability (and confusion from the IDEs).
- Use a procedural macro, which keeps the attached code readable and allow the IDE to keep performing refactoring operations on them.
This sort of situations is why I created the latter with this crate—sorry if that sounds like advertising. I'm giving a few hints for a declarative macro, too.
The problem here is the case where both types are equal, and it's unfortunately not possible to tell that Inner<T>
should be different than Inner<U>
in the blanket implementation (not that I know of, anyway). I recently added a way to do just that in that procedural macro:
use trait_gen::{trait_gen, trait_gen_if};
struct Inner<T> {
a: T,
}
#[trait_gen(T -> u8, u16, u32, u64)]
#[trait_gen(U -> u8, u16, u32, u64)]
#[trait_gen_if(!T in U)]
impl TryFrom<Inner<T>> for Inner<U> {
type Error = std::num::TryFromIntError;
fn try_from(item: Inner<T>) -> Result<Inner<U>, std::num::TryFromIntError> {
Ok(Inner::<U> { a: U::try_from(item.a)? })
}
}
It implements the code for all T
and U
types as if they were in a for
loop. The (!T in U)
prevents generating the code when T
is in one of the types on the right, so in this case, when T
is not U
, which was the problem.
I used TryFrom
above in case you need all the combinations because there's no u8::From<u16>
. You can complete the list of types as you wish, as long as they're covered by the TryFrom
; the compiler will directly tell you if a type isn't compatible (e.g. "the trait From<u8>
is not implemented for String
").
You might be able to do the same with a declarative macro, though I'm not entirely sure about the condition bit. Perhaps a pattern with an if
that ensures both types are not equal? Or simply a repetition of the macro, like what has been done for try_from.
If you just want to implement everything From<u8>
, it's simpler:
use trait_gen::trait_gen;
struct Inner<T> {
a: T,
}
#[trait_gen(T -> u16, u32, u64)]
impl From<Inner<u8>> for Inner<T> {
fn from(item: Inner<u8>) -> Inner<T> {
Inner::<T> { a: T::from(item.a) }
}
}
That's feasible with a declarative macro, too: something like impl_inner!(u16, u32, u64)
and a loop over the arguments in the macro, like the impl_try_from_upper_bounded!
of the try_from linked above.