Closures and Self and Lifetimes

pub trait Waker {
    fn wake(&self);
}

pub struct LocalWaker(pub Box<dyn Fn()>);

impl Waker for LocalWaker {
    fn wake(&self) {
        self.0()
    }
}
use crate::{future::Future, handler::SocketHandler, waker::LocalWaker};

trait Scheduler {
    type Future: Future;
    fn spawn(&mut self, task: Self::Future);
    fn run(&mut self);
}

struct LocalScheduler {
    runnable: Vec<Box<dyn Future<Waker = LocalWaker>>>,
    updated: Vec<Box<dyn Future<Waker = LocalWaker>>>,
}

impl Scheduler for LocalScheduler {
    type Future = SocketHandler;

    fn spawn(&mut self, task: Self::Future) {
        self.runnable.push(Box::new(task));
    }

    fn run(&mut self) {
        // for all the futures in runnable poll them
        // then wait on the reactor to update
        self.runnable.append(&mut self.updated);
        for run in self.runnable.drain(..) {
            run.poll(LocalWaker(Box::new(|| self.updated.push(run))))
        }
    }
}

Very new to rust - trying to figure out what is the right way to have a closure contain self. I think I understand the issue here:
Rust is complaining that the reference to self in the closure might not be valid because rust does not know exactly what the lifetime of self could be.
But how do I specify that the lifetime of the struct will be longer than the lifetime of the reference to self in the closure?

The place you're trying to put the closure is Box<dyn Fn()>, which defaults to being equivalent to Box<dyn Fn() + 'static>. This means that if the function type contains any references, they must be &'static references — things that are borrowed forever. Therefore, the only way to make your self reference live long enough would be to make it &'static mut self. That will not get you anywhere — it'll just jam things up with conflicting forever-borrows.

In a situation like this, with callbacks that may be executed at arbitrary later times, you need to arrange so that the things which the closure captures are reference-counted, not borrowed — that is how you satisfy a 'static requirement without also creating a memory leak. (Since you're apparently exploring future/task matters, I'll point out the example of the Wake trait — it has Arc built into its definition because wakers essentially always have to be reference counted.)

Since you're trying to update some state through this callback you'll also need some interior mutability; a Rc<RefCell<Vec<...>>> would do in this single-threaded world, but the job you're trying to do can also be done by a channel, such as the one in std::sync::mpsc — the wakers send items and the scheduler receives them. Either will work and the channel is arguably clearer about its purpose.

Also, you're going to have an ownership problem on that same line. You're trying to call poll() on run, after having transferred ownership of run into the closure for the waker, but at that point you no longer have ownership, so you can’t call poll(). You’ll need to change your design to do something about this. (This is not just about satisfying the rules in the abstract; it also determines what your program’s behavior is when the Future decides to invoke its Waker while it is being polled, rather than later. This is something that comes up in real async programs and an async executor needs to be prepared for it.)

Thanks for the great explanation.

Just one follow up question:

I know this still ends up with errors but could I solve this problem by specifying lifetimes like so:

pub trait Waker {
    fn wake(&mut self);
}

pub struct LocalWaker<'a>(pub Box<dyn FnMut() + 'a>);

impl<'a> Waker for LocalWaker<'a> {
    fn wake(&mut self) {
        self.0()
    }
}


use crate::{future::Future, handler::SocketHandler, waker::LocalWaker};

trait Scheduler<'a> {
    type Future: Future;
    fn spawn(&mut self, task: Self::Future);
    fn run(&'a mut self);
}

struct LocalScheduler<'a> {
    runnable: Vec<Box<dyn Future<Waker = LocalWaker<'a>>>>,
    updated: Vec<Box<dyn Future<Waker = LocalWaker<'a>>>>,
}

impl<'a> Scheduler<'a> for LocalScheduler<'a> {
    type Future = SocketHandler;

    fn spawn(&mut self, task: Self::Future) {
        self.runnable.push(Box::new(task));
    }

    fn run(&'a mut self) {
        // for all the futures in runnable poll them
        // then wait on the reactor to update
        self.runnable.append(&mut self.updated);
        for run in self.runnable.drain(..) {
            run.poll(LocalWaker(Box::new(|| self.updated.push(run))))
        }
    }
}

Just want to make sure I'm understanding this correctly that by doing this I'm telling the compiler that the lifetime of the closure will be contained within the lifetime of LocalScheduler so the passing the reference should be okay?

This will not work. &'a mut self has the type &'a mut LocalScheduler<'a>, which, because 'a is used both inside and outside the mutable reference’s referent type, has the consequence that the LocalScheduler is borrowed for the rest of its existence”. You can only do this once, and you usually don’t want to do it even once.

The key thing you need to keep in mind to avoid confusing yourself here is that lifetimes are not “of” values. They do not describe how long values exist. They describe how long particular borrowings of values last.

Avoid the phrase “the lifetime of <some value or type>”; it is confusing. Lifetimes are only “of” borrowings — particular uses (implicit or explicit) of the & operator. Types and traits contain lifetimes; reference types contain lifetimes that are the lifetimes of the borrowings that produced those references.

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.