Lifetime: how to express that the closures won't outlive the function?


#1

Howdy y’all.

So I have this run function which creates a reactor, spawns some futures and then calls core.run(). Nothing should outlive this method. However I correctly get an error saying that the closure might outlive the borrowed content, which isn’t the case.

https://play.rust-lang.org/?gist=a04421897aa077f6308b1b76704c66d8&version=stable

Thanks!


#2

spawn() requires a future that’s 'static - the compiler can’t reason about the rest of the code like you can :slight_smile:. The typical way out is to use shared ownership even if technically unnecessary. Otherwise, you’d need to step into unsafe territory and enforce lifetime invariants without borrowck.


#3

Thank you, @vitalyd. But what do you mean by shared ownership?


#4

Generally, this means using Rc or Arc, depending on threading requirements. In the case of tokio, which is a single-threaded event loop, Rc is enough.

In your case, I don’t think you need a Mutex (unless the real code does, which isn’t visible in the playground). You can wrap the HashMap in a RefCell, which is like a single-threaded mutex.

So something like this for your example:

pub struct Coordinator {
     // Rc<RefCell<>> wrapper
    sessions : Rc<RefCell<HashMap<String, String>>,
}

...
// Make a clone - this gives you an "owned" handle to the data, even though all clones reference the same
// piece of data - this is shared ownership
let user_session_clone = Rc::clone(&self.sessions);

// Add `move` so the closure takes ownership of `user_session_clone`.  Now your closure doesn't capture
// any references from its environment, and is therefore 'static
let us_f = user_sessions.for_each(move |user_session| {
    let m = &mut *user_session_clone.borrow_mut();
    m.insert( "".to_owned(), " ".to_owned() );
    future::ok( () )
});
core.handle().spawn( us_f );

#5

Parfait! Merci beaucoup!