So, normally, if you try to implement a trait in such a way, you end up with overlapping impls. However, you can overcome this by adding an additional type parameters to disambiguate:
use std::ops::Deref;
pub struct Zero;
pub struct Succ<T>(T);
pub trait TransitiveDeref<Target, Index> {
fn transitive_deref(&self) -> &Target;
}
impl<T> TransitiveDeref<T, Zero> for T {
fn transitive_deref(&self) -> &T { self }
}
impl<U, U2, T, I> TransitiveDeref<T, Succ<I>> for U
where
U: Deref<Target=U2>,
U2: TransitiveDeref<T, I>,
{
fn transitive_deref(&self) -> &T {
U2::transitive_deref(U::deref(self))
}
}
However, I'm still trying to work out the kinks...
In this case, for some reason I don't quite understand, this technique is producing a lifetime issue. You can kiiiiinda sort of work around it by modifying the trait to have a lifetime:
pub trait TransitiveDeref<'a, Target, Index>: 'a {
fn transitive_deref(&'a self) -> &'a Target;
}
impl<'a, T: 'a> TransitiveDeref<'a, T, Zero> for T {
fn transitive_deref(&'a self) -> &'a T { self }
}
impl<'a, U: 'a, U2, T: 'a, I> TransitiveDeref<'a, T, Succ<I>> for U
where
U: Deref<Target=U2>,
U2: TransitiveDeref<'a, T, I>,
{
fn transitive_deref(&'a self) -> &'a T {
U2::transitive_deref(U::deref(self))
}
}
fn foo<Index>(x: impl for<'a> TransitiveDeref<'a, i32, Index>) {
let x: &i32 = x.transitive_deref();
println!("{}", x)
}
but all of the types need to be 'static
.
fn main() {
foo(&&&&&&4); // it works if all intermediate types are 'static
let x = 4;
foo(&&&&&&x); // this won't work because it has `&'a _` where 'a != 'static
}
To get around that, you need to give up on taking impl Trait
and instead take &impl Trait
so that you can specify the lifetime in &'a self
:
fn foo<'a, Index>(x: &'a impl TransitiveDeref<'a, i32, Index>) {
let x: &'a i32 = x.transitive_deref();
println!("{}", x)
}
Playground: Rust Playground