Storing closures or equivalent


#1

Hi there,
I would like to implement a method that takes a label and a closure and stores them in a way, so that the closure can be call once the label is chosen by a user. Could you tell me an elegant way to achieve this?

Thank you very much.


#2

To store the closure, you need to box it. You’ll probably want something like (untested):

struct MyContainer {
    closures: BTreeMap<String, Box<Fn()>>,
}
impl MyContainer {
    fn store<F: Fn() + 'static>(&mut self, label: String, function: F) {
        self.closures.insert(label, Box::new(function) as Box<Fn()>);
    }
    fn call(&self, label: &str) {
        // panics if no closure has been inserted with label.
        (self.closures[label])();
    }
}

Obviously, if you you might want some better error handling (and maybe allow closures return values).


#3

Ah, that was quick, I was not aware of the +'static syntax and got refused by the compiler on my approaches.

Thank you very much :smile:


#4

If you want a slightly more flexible solution (where closures can reference the stack), you can use a custom lifetime:

struct MyContainer<'a> {
    closures: BTreeMap<String, Box<Fn() + 'a>>,
}
impl<'a> MyContainer<'a> {
    fn store<F: Fn() + 'a>(&mut self, label: String, function: F) {
        self.closures.insert(label, Box::new(function) as Box<Fn() + 'a>);
    }
    fn call(&self, label: &str) {
        // panics if no closure has been inserted with label.
        (self.closures[label])();
    }
}

F: Fn() + 'a is short for F: Fn(), F: 'a. The first constraint means "F is a closure" and the second prevents F from referencing variables on the stack that don’t outlive the lifetime 'a. Fn() + 'static sets that lifetime to 'static so it forbids closures from referencing anything on the stack.