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...


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. ↩︎