Implementing From for a struct with a generic type parameter

struct Inner<T> {
    a: T,
}

impl<T,U> From<Inner<T>> for Inner<U> 
where U: From<u8>{
    fn from(item: Inner<T>) -> Inner<U>{
        Inner::<U>{a:U::from(0)}
    }
}

This doesn't compile and the compiler complains that there's a conflicting implementation in core:

error[E0119]: conflicting implementations of trait `From<Inner<_>>` for type `Inner<_>`
 --> src/main.rs:6:1
  |
6 | / impl<T,U> From<Inner<T>> for Inner<U> 
7 | | where U: From<u8>{
  | |_________________^
  |
  = note: conflicting implementation in crate `core`:
          - impl<T> From<T> for T;

So I can kind of see where the compiler is coming from: there's already an implementation of From that takes T to T (presumably it's just the identity function?) but it's not taking into account the inner type, right? Or have I misunderstood what's going on? In any case, this feels like a case where you might want to implement your own From and the compiler won't let you? Is there a way to do what I want? The use case is I have a struct that takes a generic numerical parameter and I want to be able to map from Inner<u8> to Inner<u16> for example.

Obviously, the actual implementation here is silly, but I wanted to get something that compiles first before I actually implement the from function...

No, not at the moment. Making your implementations co-exist with the one from the standard library would require specialization.

1 Like

Ah neat. I'm actually proud I've come far enough in my Rust journey to hit up against a limitation of the current version of the language. Thanks!

Is there any way to work around this? I guess I can just write a function that does what I want (i.e. one that doesn't claim to implementing the From trait) but I wonder if there are other options currently?

Side note that even if where bounds were taken into account,[1] your implementation overlaps with e.g. From<Inner<u8>> for Inner<u8>.[2]

You could use an intermediate newtype, but I don't think there are any solid advantages. It gives you a way to use bounds like where Convert<U>: Into<Inner<u16>> or such, but such a bound seems pretty clunky / like a concrete abstraction to me.


  1. and future compatibility wasn't ↩︎

  2. today ↩︎

1 Like

I note that your From implementation doesn't actually use the item value passed in. Assuming that's intentional and not just for the example code, a better approach would be to implement Default instead of From like so:

impl<T> Default for Inner<T> 
where T: From<u8> {
    fn default() -> Inner<T> {
        Inner {a: T::from(0)}
    }
}

Thanks but that's a red herring: like I said in my original message, I just wanted something that compiles before I build out the actual implementation.

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.

2 Likes