where T could also be any T that implement Tr or Box<dyn Tr>. But Box<dyn Tr> does not implement Tr.
So I thought about implementing Tr for all types that dereference to a type that implements Tr, and I wrote a code similar to that:
trait Tr {
fn f(&self) -> &Vec<i32>;
}
impl<T, U> Tr for T
where
T: Deref<Target = U>,
U: Tr,
{
fn g(&self) -> &Vec<i32> {
(**self).g() // error U may not live long enough
}
}
But I get a compiler error: "U may not live long enough".
I do not understand this error. I believe that, in the call to Tr::g one can prove that U will live as long as the reference to T. Why the compiler generates this error?
What solution should I implement to make sure f<T> can be called for T:Tr or T=Box<dyn Tr>?
I don't know the exact reason, maybe implicit lifetime bounds doesn't apply to generic parameters? anyway, you can get away by put the bound on the associated type:
impl<T> Tr for T
where
T: Deref,
T::Target: Tr,
{
///...
}
There is some sort of shortcoming here. This works:
impl<T> Tr for T
where
T: ?Sized + Deref<Target: Tr>,
{
fn f(&self) -> &Vec<i32> {
(**self).f()
}
}
Basically the same as the comment before this one. But using a newer language feature.
When you have an implementation impl Tr for T and T: 'some_lifetime, the compiler can conclude that <T as Tr>::NonGenericAssociatedType: 'some_lifetime, because there's no way for the associated type to "see" any lifetime shorter than 'some_lifetime.[1]
And with the code snippet above, the comipler figures this out. But in your OP where you've split things up into two different generic type parameters (connected by an equality bound), the compiler apparently fails to propagate the lifetime inference across the type equality constraint.
That's my best guess at an explanation anyway.
All of that being said, it is more common to have a lot of boilerplate implementations, I believe. An uncovered blanket implementation[2] can be limiting in other ways.[3] Not sure if it matters for your use case though.
The language assumes foreign types might meet the bounds in the future even if they don't today (for forward semver compatibility reasons), so if you try to add some more specific implementations you may get "overlapping implementation" errors. ↩︎
Instead of implementing Tr for all Types with those Bounds you could also put the bounds on the function you want to call. Sadly this needs an AsRef for every type that implements Tr. I tried to get rid of it by using borrow, but then type annotations for O were needed.
If it would be fine for you to call your function with &[&YourType] then you could also do the bounds
fn take_tr<T>(arr: &[T])
where
T: Deref<Target: Tr>,
{
and then you don't need the AsRef impl for anymore.