I am stumped by an error that occurs in this example:
I have a set of types that should provide synchronized access to some type of collection, in the example its a HashMap, and I interface with them with a trait called Traversable
which has a lock
function returning a guard to access the collection through. So this guard will represent the lifetime of a lock being held.
trait Traversable {
type Guard<'g>: Deref<Target=HashMap<usize, String>> where Self: 'g;
fn lock<'g>(&'g self) -> Self::Guard<'g>;
}
Then there are types which are able to get values out of this collection, and the interface here is GetValue
which has a function get_value
expecting one of these guards, so the signature looks like this:
fn get_value<
'g,
Trav: Traversable,
>(&self, guard: &'g Trav::Guard<'g>) -> &'g String;
where guard
is the guard to the locked collection of a generic Traversable. Now the lifetimes here are supposed to enforce that the provided guard outlives the return value &'g String
, because that is supposed to be a reference into the guarded collection.
However if I try to use this like this:
fn example<
Trav: Traversable,
>(&self, trav: &Trav) {
let guard = trav.lock();
{
let value = self.get_value::<Trav>(&guard);
// do some stuff with value
println!("{}", value);
// drop value
}
// drop guard
}
It says guard
does not live long enough.
error[E0597]: `guard` does not live long enough
--> src/main.rs:54:44
|
54 | let value = self.get_value::<Trav>(&guard);
| ^^^^^^ borrowed value does not live long enough
...
58 | }
| -
| |
| `guard` dropped here while still borrowed
| borrow might be used here, when `guard` is dropped and runs the destructor for type `<Trav as Traversable>::Guard<'_>`
But shouldn't 'g
be the lifetime of the guard
variable?
No, because the lock
function asserts that fn lock<'g>(&'g self) -> Self::Guard<'g>
, so 'g is in fact the lifetime on trav: &Trav
, which may be much larger than the lifetime of the guard
variable.
I can't remove the lifetime from &'g self
in lock
, because then an implementation of lock
could not return a matching lifetime. The error I get is:
21 | fn lock<'g>(&self) -> Self::Guard<'g> {
| -- - let's call the lifetime of this reference `'1`
| |
| lifetime `'g` defined here
22 | self.entries.lock().unwrap()
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ associated function was supposed to return data with lifetime `'g` but it is returning data with lifetime `'1`
So I tried to introduce a second lifetime for representing the lifetime of the Traversable &Trav
(or &self
):
trait Traversable {
type Guard<'a: 'g, 'g>: Deref<Target=HashMap<usize, String>> + 'g where Self: 'a;
fn lock<'a: 'g, 'g>(&'a self) -> Self::Guard<'a, 'g>;
}
So here 'a must include 'g but can still be different from 'g. Now when I use it like this:
trait GetValue {
fn get_value<
'a: 'g,
'g,
Trav: Traversable,
>(&self, guard: &'g Trav::Guard<'a, 'g>) -> &'g String;
}
fn example<
'a: 'g,
'g,
Trav: Traversable,
>(&self, trav: &'a Trav) {
let guard = trav.lock();
{
let value = self.get_value::<Trav>(&guard);
// do some stuff with value
println!("{}", value);
// drop value
}
// drop guard
}
I would think 'g must be the lifetime of the variable guard
, because that is asserted by &'g Trav::Guard<'a, 'g>
in get_value. But for some reason I get the exact same error. For some reason value: &'g String
still outlives guard
, even though I would expect it to have the same lifetime at most. Actually value
is dropped before guard
, so it should have a shorter lifetime inside of 'g. But even if I introduce a third lifetime for the return value &'x String
, with 'g: 'x
('g containing 'x), I get the exact same error.
trait GetValue {
fn get_value<
'a: 'g,
'g: 'x,
'x
Trav: Traversable,
>(&self, guard: &'g Trav::Guard<'a, 'g>) -> &'x String;
}
So what exactly is going on here? I feel like I am completely on the wrong track, but it is extremely hard to google this at this point.
This is even a simplified version of what I actually need. In my actual use case I need the lifetime of the value: &String
to be either in &self
or &Trav::Guard<'g>
, but both should be allowed. So I really need to understand this even simpler problem.
If someone could point me in a direction or has encountered this before, I would be very thankful.