Suppose I'm writting a trait Tr, and I want consumers of Tr to be able to easily inherit implementations of Tr. First attempt:
trait Tr {
f(&self);
}
trait TrImpl {
type Impler: Tr;
fn make(&self) -> Self::Impler;
}
impl<T> Tr for T
where
T: TrImpl
{
fn f(&self) {
self.make().f()
}
}
Consumers can implement TrImpl to forward calls to their chosen Impler.
(imagine Tr has many methods, so it would actually be a big pain to manually forward all the methods on your own, especially given that you expect to be forwarding implementations like this for many different types.)
This works fine until you realize that often the Impler you want to use ultimately comes from a reference you own, and so will have a lifetime bound that can't outlive &self. Next attempt:
trait TrImpl<'a> {
type Impler: Tr;
fn make(&'a self) -> Self::Impler;
}
impl<T> Tr for T
where
T: for<'a> TrImpl<'a>
{
fn f(&'a self) {
self.make().f()
}
}
Great. You chug along with this for a bit... but then what if you want to implement TrImpl for a type that itself has a non-static lifetime bound 'b? There's no way to use an Impler that's going to allow the impl<T> Tr for T to work, because T: for<'a> TrImpl<'a> won't be satisfied.
I'd like to be able to say something like for<'a. 'b: 'a> TrImpl<'a>, read as "for any lifetime 'a such that 'b lives as long as 'a". I'm pretty sure that's not possible.
This type of thing will work better once generic associated types are implemented. But, before going down this path - any reason you can't provide default impls of the methods inside Tr itself? It's not quite clear why you need the TrImpl indirection. What exactly do you mean by:
For a simple example, suppose A implements Tr, my type B owns an A, and I want to implement Tr by forwarding to A's implementation.
And then on top of that, maybe there's a third type C<T> that can take a type that implements Tr and provide a new implementation with some enhancements (say, maybe by doing some logging), and I also want the option for B to use C<A> and forward calls to that.
Of course now you have to return a reference tied to self (or a static but that’s not very likely or useful), but perhaps that’s ok for your use cases.
Without the lifetime parameter, how do I handle a situation like this?
struct S<'a>(&'a T); // implements Tr; T is some other type
struct MyStruct {
t: T,
// other members
}
impl TrImpl for MyStruct {
type Impler = ??; // want it to be S<'a>, but what lifetime 'a ?
fn close<F>(&self, f: F)
where
F: FnOnce(&SelfImpler)
{
f(&S(&self.t))
}
}