This code doesn't compile, saying that the two implementations are conflicting for &_. It also doesn't compile with the new trait solver.
Can someone explain why there is a conflict, and eventually give an example code where there is an ambiguity on the impl to use?
I couldn't find a case where the two implementations are conflicting.
I don’t have a good way to explain exactly how RecursiveAsRef conflicts, but the way to solve your problem with MyTrait is to implement that trait for references, instead of having the separate RecursiveAsRef trait. That is:
because this implementation provides an impl for &&MyStruct given that you wrote impl MyTrait for &MyStruct. In general, it is good practice to implement a trait for references and other pointers (&, &mut, Box, Rc, Arc...) whenever the shape of the trait permits. (However, having these impls does mean that you can't implement bothimpl MyTrait for MyStruct and impl MyTrait for &MyStruct, so if you have a use for that, it might be not the best option.)
This should be the solution, because I think it gives you exactly what you want without changing your self API instead of using &self, and basically just chains indefinitely like you want.
Ok, thank you all for your answers; I think I'll do what you suggested, even if it's a bit annoying to write the same generic implementation for every single trait I write (maybe some macros exist?)
However, I still don't know why the two implementations are conflicting.
I'll remove the "context" part of my message: it's not really relevant here. Feel free to see the history of my edits to see the context part.
Removing the lifetimes for simplicity, it's a pretty subtle conflict, so it's easier to explain without the lifetimes.
Basically both impls claim that &X: RecursiveAsRef<&X>, you have overlapping implementations, I am guessing the compiler can't decide which one it should use?
Not sure what do you mean by "example code", since it's fairly simple:
struct Unit;
let unit_ref = &Unit;
unit_ref.to_ref();
...with the ambiguity being impl RecursiveAsRef for T with T = &Unit or impl RecursiveAsRef for &T with T = Unit.
Actually, isn't it an example of the first common lifetime misconception, that is, thinking that impl<T> SomeTrait for T somehow forces T to not be a reference?
Ok, I think I see what you mean, but I didn't really understand how both of the impl satisfies &X: RecursiveAsRef<&X>.
Without considering the lifetime, and sticking with the implementations of my first message:
The first implements &X: RecursiveAsRef<&X>. Simple.
But for the second impl to implement &X: RecursiveAsRef<&X> too, there would have to be [ T = &X ] and [ S = X ] and [ S: RecursiveAsRef<T> ].
The last requirement is the same as X: RecursiveAsRef<&X>, if we replace the generics.
However, the X: RecursiveAsRef<&X> requirement is never satisfied (there is no implementation of RecursiveAsRef<&X> for X). So the three requirements can't be all satisfied, and the second impl can't implement &X: RecursiveAsRef<&X>.
So there should be no conflict right?
I don't know if it's clear enough, but that's how I see the thing.
I think it's basically because how the compiler checks coherence,
Coherence also results in an error if any other impls could exist, even if they are currently unknown. This affects impls which may get added to upstream crates in a backwards compatible way, and impls from downstream crates. This is called the Orphan check.
Edit: Nothing wrong with your mental model or logic, it's just not aligned with how the compiler is designed.