Implementation is not general enough - "... is actually implemented for some specific lifetime"

Your implementation of NewResourceUser is as such:

impl<'a> NewResourceUser<MyResourceUser<'a>> for &'a MyResource {

So &'a MyResource only implements NewResourceUser<MyResourceUser<'a>> -- the lifetimes have to be the same.

But the supertrait requirement on Factor resolves like so:

for<'a, 'r> &'r MyResource: NewResourceUser<MyResourceUser<'a>>

That is, you'd have to be able to get a MyResourceUser<'a> out of any &'r MyResource, even though they have unrelated lifetimes. That's not possible, so that supertrait bound isn't what you want.


I think you really wanted U to be a type constructor;[1] that is, you wish you could have

// Made up syntax/feature
trait Factory<R, U<'*>> where for <'a> &'a R: NewResourceUser<U<'a>> {

But Rust doesn't have those. Type parameters like U must resolve down to a single type -- that means all lifetimes must resolve too. Just like U can't represent Vec<X> for any X, U can't represent MyResourceUser<'x> for any 'x.

If any given R only needs to be associated with a specific type constructor like MyResourceUser<'_>, and if object safety isn't a concern, you could make the type constructor a GAT in NewResourceUser.

However it won't really work with the requirement to be able to choose U...

[2]

So, maybe you do really need to emulate "type constructor generics". One way to do this is to have a trait with a GAT...

// A type constructor for NewResourceUser<impl Resource>::new_user
trait Resource {
    type Ty<'a>;
}

...then implement the trait for marker types that will represent the type constructor with any lifetime...

struct MyResourceUserMarker;
impl Resource for MyResourceUserMarker {
    type Ty<'a> = MyResourceUser<'a>;
}

...and use type parameters with a : Resource bound...

trait NewResourceUser<U: Resource> {
    fn new_user(&self) -> <U as Resource>::Ty<'_>;
}

trait Factory<R, U> where R: NewResourceUser<U>, U: Resource {
    fn new_resource() -> R;
}

Like so:


  1. something that is parameterized by generic lifetimes and/or traits ↩︎

  2. Nit (or fundamental understanding adjustment): the lifetime limitation will be based on standard borrowing considerations in this scenario (fn fru in my playground below), i.e., the result of r.new_user() will borrow r and won't be able to outlive r being moved, mutably borrowed, or destructed; the type of r/R is incidental. ↩︎

2 Likes