Managing lifetimes in closure

Hi !

I'm trying to make working my lifetimes without success. There is an sample code :

use std::sync::{Arc, RwLock, RwLockWriteGuard};

struct MyStruct;

struct State(Arc<RwLock<Vec<MyStruct>>>);

struct Processor {
    state: Arc<State>,
}

impl Processor {
    fn process<T, F>(&self, get: F)
    where
        F: for<'a> Fn(&'a mut RwLockWriteGuard<'a, Vec<MyStruct>>, usize) -> &'a mut T,
    {
        let mut structs = self.state.0.write().unwrap();
        let _ = get(&mut structs, 42);
    }
}

fn main() {
    let objects = vec![];
    let state = Arc::new(State(Arc::new(RwLock::new(objects))));
    let processor = Processor { state };
    processor.process(|s, i| &mut s[i]);
}
error[E0597]: `structs` does not live long enough
  --> src/main.rs:17:21
   |
16 |         let mut structs = self.state.0.write().unwrap();
   |             ----------- binding `structs` declared here
17 |         let _ = get(&mut structs, 42);
   |                     ^^^^^^^^^^^^ borrowed value does not live long enough
18 |     }
   |     -
   |     |
   |     `structs` dropped here while still borrowed
   |     borrow might be used here, when `structs` is dropped and runs the `Drop` code for type `std::sync::RwLockWriteGuard`

I reduced my real code in this example. I have a function process which take a closure get which must be able to take a mutable reference from a RwLock inside it.

So, the get closure must return a reference which live same lifetime than reference inside RwLockWriteGuard.

I tried a lot of combination of lifetime without success. Lifetimes are a difficult part for me ... :smiley:

Any help ?

I found a working solution by random tries ...

impl Processor {
    fn process<T, F>(&self, get: F)
    where
        F: for<'a> Fn(&'a mut Vec<MyStruct>, usize) -> &'a mut T,
    {
        let mut structs = self.state.0.write().unwrap();
        let x = get(&mut structs, 42);
    }
}

I take an explanation if someone have it !

the problem is the &'a mut RwLockWriteGuard<'a, ...> type, which is a common source for lifetime related errors, because mut references are invariant. see:

try something like this:

    fn process<T, F>(&self, get: F)
    where
        F: for<'a, 'b> Fn(&'a mut RwLockWriteGuard<'b, Vec<MyStruct>>, usize) -> &'a mut T
    {
        //...
    }

or this:

    fn process<T, F>(&self, get: F)
    where
        F: for<'a> Fn(&'a mut RwLockWriteGuard<'_, Vec<MyStruct>>, usize) -> &'a mut T
    {
        //...
    }
2 Likes

The lifetime parameter of the RwLockWriteGuard is the lifetime of its borrow of the RwLock. So the most specific, informative lifetime annotation you could use on your original code is:

impl Processor {
    fn process<'s, T, F>(&'s self, get: F)
    where
        F: for<'a> Fn(&'a mut RwLockWriteGuard<'s, Vec<MyStruct>>, usize) -> &'a mut T,
    {

However, for most purposes, your for<'a> Fn(&'a mut Vec<MyStruct>, usize) -> &'a mut T is a better choice, because it is simpler to understand, and the function doesn't need to know about the lock guard at all.

1 Like

Thank you for your explanations ! That's a complicated side for me. I will study that.

Incidentally, if it's possible to call the closure exclusively -- which is usually the case for closures you take as arguments and drop at the end of the method -- it's kinder to the caller if you take a FnMut instead of a Fn.

And if you're only ever going to call it once, it's even kinder to take a FnOnce.