3

This is a follow-up to my question on how to create & use a list of callbacks.

I'm trying to create (and store, near an event loop) a list of callback functions that will be called at some indeterminate point in the future.

struct ComplexThing {
    calls: Vec<Box<FnMut()>>,
}


impl ComplexThing {
    fn call<'a, T: FnMut() + 'a>(&'a mut self, func: T) {
        self.calls.push(Box::new(func));
    }
}

Errors with:

calls.rs:30:25: 30:39 error: the parameter type `T` may not live long enough [E0310]
calls.rs:30         self.calls.push(Box::new(func));
                                    ^~~~~~~~~~~~~~
calls.rs:30:39: 30:39 help: consider adding an explicit lifetime bound `T: 'static`...
calls.rs:30:25: 30:39 note: ...so that the type `T` will meet its required lifetime bounds
calls.rs:30         self.calls.push(Box::new(func));
                                    ^~~~~~~~~~~~~~

I tried adding it to the struct, which fixed the error about lifetimes on the call to push,

struct ComplexThing<'a> {
    calls: Vec<Box<FnMut() + 'a>>,
}


impl ComplexThing {
    fn call<'a, T: FnMut() + 'a>(&'a mut self, func: T) {
        self.calls.push(Box::new(func));
    }
}

… but gets me:

calls.rs:28:6: 28:18 error: wrong number of lifetime parameters: expected 1, found 0 [E0107]
calls.rs:28 impl ComplexThing {
                 ^~~~~~~~~~~~

Which, yes, I suppose the struct has a <'a>, and I'm not specifying it. If I add it,

impl ComplexThing<'a> {

I get,

calls.rs:28:19: 28:21 error: use of undeclared lifetime name `'a` [E0261]
calls.rs:28 impl ComplexThing<'a> {

I don't know if I should be specifying it on the struct ComplexThing or not. If I leave it off (and I would greatly prefer to, I think),

I think there's something crucial about how Rust notates lifetimes that I'm not getting here. The FnMut that ComplexThing is (presently) trying to store in a Box is (in the design in my head) owned by the instance of ComplexThing; it's lifetime should be less than that of .ComplexThing — i.e., one of two things would happen:

  1. What will be a private function of the ComplexThing will end up removing the Box<FnMut> from the Vec (and thus, take ownership of it), run the FnMut, and then exit, thus freeing the FnMut.
  2. The ComplexThing is deallocated, in which case the Vec and any Box<FnMut>'s are deallocated with it.

The question "How do I store a closure in Rust?"'s answer made me think a Box<FnMut> wouldn't need lifetime annotations, but the answer I got on how to create & use a list of callbacks makes me think I do.

My best guess is that Box is just storing a pointer to an object that I don't really own, and that I need to either create a copy of the FnMut on the heap, or move-construct one there, and then that copy is the one I can own. (Otherwise, if it's like a closure that's on the stack, I need to make sure that closure doesn't go out of scope before my Box does, which is why Rust is having me annotate lifetimes.)

Community
  • 1
  • 1
Thanatos
  • 42,585
  • 14
  • 91
  • 146
  • `Box` is sugar for `Box`, which explains the first error completely. `&'a Trait` is sugar for `&'a (Trait + 'a)`, incidentally. – Chris Morgan Mar 12 '15 at 07:45

1 Answers1

3

'a on ComplexThing is a generic lifetime parameter and needs to be defined just as much as a generic type parameter. If you had a struct Foo<T>, you would not be able to write impl Foo<T> because there is no concrete type T in scope; if you want it to be generic, you need to define it, impl<T> Foo<T>. This allows you to write constraints too, such as impl<T: Clone> Foo<T> to implement methods on Foo<T> only where T is a type that implements Clone.

So then, the answer is simply that you need to define the lifetime 'a as generic:

impl<'a> ComplexThing<'a> { … }
Chris Morgan
  • 86,207
  • 24
  • 208
  • 215