1

There is already a very popular question about this topic but I don;t fully understand the answer.

The goal is:

I need a list (read a Vec) of "function pointers" that modify data stored elsewhere in a program. The simplest example I can come up with are callbacks to be called when a key is pressed. So when any key is pressed, all functions passed to the object will be called in some order.

Reading the answer, it is not clear to me how I would be able to make such a list. It sounds like I would need to restrict the type of the callback to something known, else I don't know how you would be able to make an array of it.

It's also not clear to me how to store the data pointers/references.

Say I have

struct Processor<CB>
where
    CB: FnMut(),
{
    callback: CB,
}

Like the answer suggests, I can't make an array of processors, can I? since each Processor is technically a different type depending on the generic isntantiation.

2 Answers 2

5

Indeed, you can't make a vector of processors. Usually, closures all have different, innominable types. What you want instead are trait objects, which allow you to have dynamic dispatch of callback calls. Since those are not Sized, you'd probably want to put them in a Box. The final type is Vec<Box<dyn FnMut()>>.

fn add_callback(list: &mut Vec<Box<dyn FnMut()>>, cb: impl FnMut() + 'static) {
    list.push(Box::new(cb))
}

fn run_callback(list: &mut [Box<dyn FnMut()>]) {
    for cb in list {
        cb()
    }
}

see the playground


If you do like that, however, you might have some issues with the lifetimes (because your either force to move-in everything, or only modify values that life for 'static, which isn't very convenient. Instead, the following might be better

#[derive(Default)]
struct Producer<'a> {
    list: Vec<Box<dyn FnMut() + 'a>>,
}

impl<'a> Producer<'a> {
    fn add_callback(&mut self, cb: impl FnMut() + 'a) {
        self.list.push(Box::new(cb))
    }
    
    fn run_callbacks(&mut self) {
        for cb in &mut self.list {
            cb()
        }
    }
}

fn callback_1() {
    println!("Hello!");
}

fn main() {
    let mut modified = 0;
    let mut prod = Producer::default();
    prod.add_callback(callback_1);
    prod.add_callback(
        || {
            modified += 1;
            println!("World!");
        }
    );
    prod.run_callbacks();
    drop(prod);
    println!("{}", modified);
}

see the playground

Just a few things to note:

  1. You manually have to drop the producer, otherwise Rust will complain that it will be dropped at the end of the scope, but it contains (through the closure) an exclusive reference to modified, which is not ok since I try to read it.
  2. Current, run_callbacks take a &mut self, because we only require for a FnMut. If you wanted it to be only a &self, you'd need to replace FnMut with Fn, which means the callbacks can still modify things outside of them, but not inside.
2
  • Just for completeness, how would you call these objects ina loop?
    – Makogan
    Commented Aug 24, 2022 at 6:07
  • @Makogan I added an example of that too.
    – jthulhu
    Commented Aug 24, 2022 at 6:12
2

Yes, all closures are differents type, so if you want to have a vec of different closure you will need to make them trait objects. This can be archieve with Box<dyn Trait> (or any smart pointer). Box<dyn FnMut()> implements FnMut(), so you can have Processor<Box<dyn FnMut()>> and can make a vec of them, and call the callbacks on them: playground

Not the answer you're looking for? Browse other questions tagged or ask your own question.