Code design for "partial trait implement"

For example:


trait Foo {}
trait Bar {}

trait Baz {}

impl<T: Foo> Baz for T {

}

impl <T:Bar> Baz for T {

}

the compiler will report error of conflicting implementations of trait "Baz".

I searched online and got pointed that if I'm doing so, I'm still thinking generic as a SFINAE way, which is wrong.

But in my case I don't how many types are implement as Foo or Bar, and even I know, there could be more types be implement in the future, there's no way to write macros for them.

So what should I do to accomplish this code?

P.S. I remembered that impl for a struct can have conditional implements, why this don't work on traits?

I'm not sure what exactly it is you're looking for. Could you clarify your post?

If the issue is that there can be types outside of your own crate that implement these traits, well that's pretty core to how traits work (specifically the orphan rule comes to mind). And the crates defining those types will indeed have to provide an impl.

If the issue is that you want to provide both blanket impls at the same time, I'm inclined to think that's simply not possible due to the conflicts mentioned by the compiler. If there exists a workaround I'm not aware of it, but I expect it'll be trait-heavy and kind of messy, thus obscuring intent (trait-heavy code, especially when involving traits with generics and/or associated types) has a nasty habit of doing that).

The fundamental issue is that the compiler won't be able to tell which implementation of Baz should be used for a type like this:

struct S;

impl Foo for S {}
impl Bar for S {}

If you want to have multiple blanket implementations of Baz, you'll need to make sure the compiler can always figure out which one should apply. One way to do this is with an additional trait that selects one of the blanket implementations:

trait Foo {}
trait Bar {}
trait Baz {}

trait AsBaz {
    type Baz: Baz + ?Sized;
    fn as_baz(&self)->&Self::Baz;
}

impl<'a, T:AsBaz> Baz for T { /* forward methods */ }
impl Baz for dyn Foo + '_ { /* ... */ }
impl Baz for dyn Bar + '_ { /* ... */ }

struct S;

impl Foo for S {}
impl Bar for S {}

impl AsBaz for S {
    type Baz = dyn Foo;
    fn as_baz(&self)->&(dyn Foo + 'static) { self }
}

As an alternative to some conversion trait (the AsBaz from @2e71828's answer), you could also use a new type to break the ambiguity.Then for convenience you can add conversion functions which wrap your impl Foo in the newtype and return an impl Baz.

trait Foo {}
trait Bar {}

trait Baz {}

fn foo_to_baz(foo: impl Foo) -> impl Baz {
  struct FooWrapper<T>(T);

  impl<T: Foo> Baz for FooWrapper<T> { ... }

  FooWrapper(foo)
}
1 Like

I'm indeed wants the function name of the trait be the same, since this is a library, to much workaround will make the lib unfriendly to users.

Currently I use macros to implement all types I know, and wait for the feature "specialization" to be finished.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.