How to work around these conflicting implementations?

I'm trying to make an encoding into bytes & decoding from bytes trait, around Zerocopy's traits. The difference is that Zerocopy requires aligned types (and these was something impossible with unaligned features), but I have large data volumes, and alignment bytes will bloat it a good deal.

In my first implementation, I relied upon using same names for functions: read_from_io/write_to_io. This worked in straightforward implementations, and with macros, where in both cases I just had to output code like

fn write_to_io(...) -> ... {
    self.member1.write_to_io(...)?;
    self.member2.write_to_io(...)?;
}

Then I wanted something more abstract with blanket implementation, but turns out it's very hard, because impl ... for T conflicts with impl<T> ... for Option<T>.

trait Foreign: Sized {
	fn read_from_io(&self) -> Vec<u8>;
} // from external crate

trait MyTrait {
	fn read_from_io(&self) -> Vec<u8>;
}

impl<T: Foreign> MyTrait for T {
	// should call do_the_job
	fn read_from_io(&self) -> Vec<u8> { todo!() }
}

// Foreign isn't implemented for Option<T>. So I try implementing it.
impl<T: Foreign> MyTrait for Option<T> {
	fn read_from_io(&self) -> Vec<u8> { todo!() }
}

// conflicting implementations!
impl<T: MyTrait> MyTrait for Option<T> {
	fn read_from_io(&self) -> Vec<u8> { todo!() }
}

If I de-duplicate and remove the T: Something condition, then of course nothing of Zerocopy or my traits is available in the impl.

I thought of making separate traits, but then how do I call methods on them in generic code? In every way, there's a trait that has to be implemented separately for T of 2 different traits.

For example, if I want to implement write_to_io for specific tuples, (T,), (T, U), etc. I still have to implement this conversion as a trait, again both for T and Option<T>, and it's again conflicting implementation.

trait IntoData {
    type Data;
    fn into_data(&self) -> Self::Data;

Specialization isn't coming soon. So...

What I'm thinking of is to resort to the first approach, where I just made the same method names as in Zerocopy's traits, and relied on macros to write them for members.

Please suggest some workarounds if you have.

I may be missing something (from Foreign not actually being foreign in the playground) Is there any reason you need the middle impl for Option<T: Foreign>?

Is the Option<T: Foreign> impl going to do something more direct/optimized, or can it just flow logically through the third Option<T: MyTrait> impl where that implementation relies on the first impl <T: Foreign> MyTrait for T

The reason is that Foreign trait is zerocopy IntoBytes/FromBytes, and it's not implemented for Option<T>, and it's forbidden to do another impl MyTrait for T's without it.

note there already is impl IntoBytes for Option<NonZeroI8> for example.
so it's not even just hypothetical, there are already existing conflicts.

zerocopy is allowed to add more impl for IntoBytes whenever it wants for more Option<U>s, and this would not be a breaking change. thus you cannot write code that relies on the fact that for example Option<u8> is not IntoBytes.

the only thing i can think of that might help in the future would be something like zerocopy doing

impl<T : FromBytes> !FromBytes for Option<T>  {}

but negative coherence still seems a long way away, and

impl<T : IntoBytes> !IntoBytes for Option<T>  {}

would be wrong, because of types like NonZeroI8 which implement it both in and out of Option.

I'm probably missing something, just what I was trying to explore was instead of both items listed below, why not just the first item.

MyTrait implemented for:

  1. Container<T: MyTrait> which is then compatible with Foreign via the blanket impl MyTrait for T where T: Foreign
  2. Container<T: Foreign>

The second impl (foreign trait in foreign container) has issues, but what do you loose if you only use the first item in the list?

Nevermind, I was mixing up local trait with a local wrapper struct. I assume it's a no-go to impl MyTrait for Option<MyStruct<T: MyTrait>>?

For the newtype workaround I believe you have in mind, they'd need a local type as the implementor. Option<LocalTy<..>> is still a foreign type.