Lifetime puzzle (hidden type captures)

I have this sample code:

Which doesn't compile:

error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds
  --> src/
24 |     pub fn e(&self) -> MyEndpoint<impl Registrar + '_> {
   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: hidden type `std::sync::MutexGuard<'_, MyRegistrar<'a>>` captures the lifetime `'a` as defined on the impl at 23:6
  --> src/
23 | impl<'a> EndpointState<'a> {
   |      ^^

I don't have a clue on what's the compiler is trying to tell me :-/

I feel like lifetime is the next big step to climb for me… simple cases are easy enough, but sometime, weirder cases like these bite me ^^'

Ultimately it boils down to this being ok:

// OK!
pub fn e<'b>(&'b self) -> MyEndpoint<MutexGuard<'b, MyRegistrar<'a>>>

But this is not ok:

// Doesn't work!
pub fn e<'b>(&'b self) -> MyEndpoint<MutexGuard<'b, MyRegistrar<'b>>>

Thank you Alice!

So if I get it properly, impl Registrar + '_ is transformed, because of the type of what I'm putting in the field r, to MutexGuard<'b, MyRegistrar<'b>>?

Is there a way to keep the impl Registrar notation and fill in the correct lifetimes? (Just out of curiosity)

Maybe, but I couldn't find a way to make it happen. This seems like it works:

pub fn e<'b: 'a>(&'b self) -> MyEndpoint<impl Registrar + 'b>

But that's because it forces 'a = 'b, so it's a trivial case and not useful.

Could you provide more detail as for what you're trying to do? Sometimes when the borrow-checking becomes to complex, that's because the ownership model needs to be reorganized, but it's hard to tell from this example how it should be used. In particular, Registrar is currently a marker trait, but I assume it should be using f somehow? And why does the Connection need to be borrowed?

This one compiles: but it's quite different than yours, especially in that Connection isn't borrowed anymore.

This indeed also works,

I don't think it limits the usefulness that far, 'a can still be larger than 'b:
But this is of course not very useful due to the Mutex.

I was trying to pool my ldap connections and pass a connection down to oxide-auth Registrar trait implementation and another one. In the end, instead of anotate the code with many lifetime and having the code complexify, I ended passing the connection pool instead as r2d2 Pool is an Arc anyway, easier to pass around :slight_smile:

I don't know oxide-auth well enough, but I'm happy you found your solution :slight_smile:

1 Like