anderspitman
anderspitman

Reputation: 10510

Is it possible to return a closure that is stored in self?

I'm using a trait to implement something like the template method pattern. During parts of the algorithm execution, I'd like it to be able to invoke a callback to report its progress. The way I've been trying to make this work is by storing a Fn() on my structs that implement the trait, and then providing a get_callback method to provide the callbacks to the default methods on the trait. Unfortunately it's not working.

I suspect the solution will involve some combination of putting the callback in a Box and possibly returning a reference, but I haven't been able to get it to work. Here's a minimal non-working example:

trait MyTrait<T: Fn()> {
    fn default(&self) {
        let cb = self.get_callback();
        cb();
    }

    fn get_callback(&self) -> T;
}

struct MyStruct<T: Fn()> {
    callback: T,
}

impl<T: Fn()> MyTrait<T> for MyStruct<T> {
    fn get_callback(&self) -> T {
        self.callback
    }
}

fn main() {
    let x = MyStruct { callback: || {} };
    x.default();
}
error[E0507]: cannot move out of borrowed content
  --> src/main.rs:16:9
   |
16 |         self.callback
   |         ^^^^ cannot move out of borrowed content

Upvotes: 3

Views: 97

Answers (1)

mcarton
mcarton

Reputation: 30001

There is nothing special about the closure here. Your problem is this:

fn get_callback(&self) -> T {
    //          ^ you are borrowing self
    //                    ^ but you are not returning a reference
    self.callback
    //   ^ so callback needs to be moved out
}

A simple solution would be to clone the closure instead:

impl<T: Fn() + Clone> MyTrait<T> for MyStruct<T> {
    fn get_callback(&self) -> T {
        self.callback.clone()
    }
}

Cloning closures is only stable as of Rust 1.26 (see playground example).

Another solution would be to actually move out:

impl<T: Fn()> MyTrait<T> for MyStruct<T> {
    fn get_callback(self) -> T {
    //              ^ no reference
        self.callback
    }
}

This works on stable but requires a few other changes and modifying the trait.

But most likely, the solution you want is to return a reference to the closure:

fn get_callback(&self) -> &T {
    &self.callback
//  ^ there
}

But this also requires to change the trait.

Upvotes: 6

Related Questions