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.