How to fix associated type lifetime issues when chaining two trait calls together that are generic over a lifetime parameter?

I'm trying to build a "field handle" abstraction which is basically something that's built once and stored; then later you can pass a shared reference to something to get a shared reference back.

The key property I'm trying to preserve is that the trait itself doesn't really need to know about lifetimes, except that the input to the methods (get and get_mut) only need to outlive the use of the borrowed input.

My trouble is in trying to come up with a way to chain two concrete impls - I want that ability because it makes generating these things a lot easier.

// The idea is that you build this, store it somewhere, and when it come times to read some data
// (or mutate it), you can simply call this method on the original object
pub trait FieldHandle {
    type Source;
    type Dest;

    fn get<'a>(&self, source: &'a Self::Source) -> &'a Self::Dest;
    fn get_mut<'a>(&self, source: &'a mut Self::Source) -> &'a mut Self::Dest;
}


struct ChainedExtractor<S, D>(S, D);

impl <S, D> FieldHandle  for ChainedExtractor<S, D>
where 
    S: FieldHandle, 
    D: FieldHandle<Source=S::Dest>
{
    type Source = S::Source;
    type Dest = D::Dest;

    fn get<'a>(&self, source: &'a Self::Source) -> &'a Self::Dest {
        self.1.get(self.0.get(source))
    }
    
    fn get_mut<'a>(&self, source: &'a mut Self::Source) -> &'a mut Self::Dest {
        self.1.get_mut(self.0.get_mut(source))
    }
}

fn main() {
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0309]: the associated type `<S as FieldHandle>::Dest` may not live long enough
  --> src/lib.rs:22:16
   |
22 |         self.1.get(self.0.get(source))
   |                ^^^
   |
   = help: consider adding an explicit lifetime bound `<S as FieldHandle>::Dest: 'a`...
   = note: ...so that the reference type `&<S as FieldHandle>::Dest` does not outlive the data it points at

error[E0309]: the associated type `<S as FieldHandle>::Dest` may not live long enough
  --> src/lib.rs:22:20
   |
22 |         self.1.get(self.0.get(source))
   |                    ^^^^^^^^^^^^^^^^^^
   |
   = help: consider adding an explicit lifetime bound `<S as FieldHandle>::Dest: 'a`...
   = note: ...so that the type `<S as FieldHandle>::Dest` is not borrowed for too long

error[E0309]: the associated type `<S as FieldHandle>::Dest` may not live long enough
  --> src/lib.rs:22:27
   |
22 |         self.1.get(self.0.get(source))
   |                           ^^^
   |
   = help: consider adding an explicit lifetime bound `<S as FieldHandle>::Dest: 'a`...
   = note: ...so that the reference type `&<S as FieldHandle>::Dest` does not outlive the data it points at

error[E0309]: the associated type `<S as FieldHandle>::Dest` may not live long enough
  --> src/lib.rs:26:16
   |
26 |         self.1.get_mut(self.0.get_mut(source))
   |                ^^^^^^^
   |
   = help: consider adding an explicit lifetime bound `<S as FieldHandle>::Dest: 'a`...
   = note: ...so that the reference type `&mut <S as FieldHandle>::Dest` does not outlive the data it points at

error[E0309]: the associated type `<S as FieldHandle>::Dest` may not live long enough
  --> src/lib.rs:26:24
   |
26 |         self.1.get_mut(self.0.get_mut(source))
   |                        ^^^^^^^^^^^^^^^^^^^^^^
   |
   = help: consider adding an explicit lifetime bound `<S as FieldHandle>::Dest: 'a`...
   = note: ...so that the type `<S as FieldHandle>::Dest` is not borrowed for too long

error[E0309]: the associated type `<S as FieldHandle>::Dest` may not live long enough
  --> src/lib.rs:26:31
   |
26 |         self.1.get_mut(self.0.get_mut(source))
   |                               ^^^^^^^
   |
   = help: consider adding an explicit lifetime bound `<S as FieldHandle>::Dest: 'a`...
   = note: ...so that the reference type `&mut <S as FieldHandle>::Dest` does not outlive the data it points at

error: aborting due to 6 previous errors

For more information about this error, try `rustc --explain E0309`.
error: could not compile `playground`

To learn more, run the command again with --verbose.

Is it acceptable to limit the S in ChainedExtractor<S, D> to be 'static, so it can't be or contain a non-static reference? This resolves the errors:

-    S: FieldHandle,
+    S: FieldHandle + 'static,

Edit: I don't have a good idea of how FieldHandle would actually be used, but adding some constraints between the &self borrow and the &Source borrow also satisfies the borrow checker.

    fn get<'s: 'a, 'a>(&'s self, source: &'a Self::Source) -> &'a Self::Dest;
    fn get_mut<'s: 'a, 'a>(&'s self, source: &'a mut Self::Source) -> &'a mut Self::Dest;
1 Like

Thanks! I will try both of those and see where they get me (well, hopefully just one, but I'm not sure how that'll go).

I'm aiming to make a reactive GUI library. The idea is that the user makes a bunch of these field object handles, and then there's a system that asks the GUI to update itself appropriately passing the render widget (which has these handles stored) the root of the data tree that the GUI then calls the handles on to get the data it needs. So definitely convoluted, but I'm hoping it'll turn out okay.

I found another way that works! I can add a where clause to the method specifying that both source and dest are bound by 'a, e.g.

fn get<'a>(&self, source: &'a Self::Source) -> &'a Self::Dest
    where Self::Source: 'a, Self::Dest: 'a;