Vector of pointers to struct impl methods

Hello and thanks in advance for taking the time to read. My question in simple, I have the following struct:

struct Foo {
    x: u8,
    vector: Vec<Box<dyn Fn()->()>>
}

impl Foo {
    fn a(&mut self) -> () {
        self.vector.push(Box::new(|| self.b()));
    }

    fn b(&mut self){
        self.x = 5
    }
}

The compilers throws the following error among ohters:

error[E0596]: cannot borrow `*self` as mutable, as it is a captured variable in a `Fn` closure
    |
434 |         self.vector.push(Box::new(|| (*self).b()));
    |                                      ^^^^^^^^^^^ cannot borrow as mutable

error: lifetime may not live long enough
433 |     fn a(&mut self) -> () {
    |          - let's call the lifetime of this reference `'1`
434 |         self.vector.push(Box::new(|| (*self).b()));
    |                          ^^^^^^^^^^^^^^^^^^^^^^^^ cast requires that `'1` must outlive `'static`

My intent with this approach is to build a vector of functions pointers of struct associated methods.
What I'm missing here? I don't exactly get what is wrong with the lifetime specifier of self in this case. If someone could clarify me on this topic it would be of great help.

Rust does not support self-referential structs, which are structs where one field contains a reference to the struct (or another field of the struct). In your case, the method you are trying to add to the vector contains a reference to the Foo, so it violates this rule.

1 Like

A closure such as || self.b(), or with the method-call desugared, || Foo::b(self), (where self: &mut Foo) is more than just a function pointer. A closure “closes” the variables you mention in its body by capturing them. The closure || Foo::b(self) mentions the local variable self, so the closure || Foo::b(self) is essentially a struct containing that captured variable. Closures combine data + behavior, the data is the captured variables and the behavior is encoded in the way they implement one of the Fn* traits. The behavior of the || Foo::b(self) closure is in how it implements the FnOnce trait’s call_once method by obtaining the captured self variable from the closure and calling Foo::b on it.

Now there’s two error messages here indicating two different problems.

The first problem is that in order to use the captured &mut Foo variable, you need mutable access to it, but the Fn trait’s call method only gives immutable access to captured variables. This closure can only implement FnOnce and FnMut, but not Fn as required by the type of the vector field.

The second problem is that the closure contains the self reference to the Foo struct itself. You cannot put the closure into the Foo struct because it can’t contain (a value containing) a reference to itself.[1] This is what @alice is eluding to with the term “self-referential structs”, and this is the reason for the second error message which talks about lifetimes.

Lifetimes are involved in the precise process of how the compiler stopped you from creating a self-referential struct here: In this case, your vector field has a type that isn’t allowed to contain any short-lived (i.e. non-'static) references in the first place, so that’s where putting the closure containing the captured self: &mut Foo variable into the vector field already fails. This restriction is indicated by the 'static in the trait bound dyn Fn() -> () + 'static; and Box<dyn Fn() -> ()> is a shorthand for Box<dyn Fn() -> () + 'static> for convenience. Note that you cannot simply fix the code by introducing a lifetime argument here because there’s no way to name a concept like “the lifetime of the struct itself” or something along these lines in the definition of struct Foo.


  1. except perhaps for immutable references to static variables, or using shared-ownership via Arc/Rc; but you’d also need to use interior mutability like e.g. RefCell for this in either case ↩︎

2 Likes

As for alternative approaches that could work, depending on your use-case: If you intend to collect actual function pointers, and all of those are supposed to be (&mut self) methods of Foo, or at least should have the option to work with &mut self of the Foo, you could just collect fn(&mut Foo) function pointers. If you want to generalize to closures that shall be able to capture other kinds of things, then Box<dyn FnMut(&mut Foo)> is an option, but it becomes harder to call those functions, because calling them would usually involve a mutable borrow of their respective self.vector-item, so mutably borrowing the whole self struct at the same is impossible. One approach to solve the issue is to take-out-and-put-back-later the entire vector (or possibly just a single item).

1 Like

Thank you! Your alternative was exactly what I was looking for, thanks for all the explanations.