带数据的通用回调

Mak*_*gan 1 events callback rust

关于这个主题已经有一个非常受欢迎的问题,但我不完全理解答案。

目标是:

我需要一个“函数指针”列表(读取 Vec)来修改存储在程序中其他位置的数据。我能想到的最简单的例子是按下按键时调用的回调。因此,当按下任意键时,传递给该对象的所有函数都将按某种顺序调用。

阅读答案后,我不清楚如何才能列出这样的列表。听起来我需要将回调的类型限制为已知的类型,否则我不知道如何创建它的数组。

我也不清楚如何存储数据指针/引用。

说我有

struct Processor<CB>
where
    CB: FnMut(),
{
    callback: CB,
}
Run Code Online (Sandbox Code Playgroud)

就像答案所暗示的那样,我无法制作处理器阵列,可以吗?因为每个处理器在技术上都是不同的类型,具体取决于通用实例。

Bla*_*ans 5

事实上,你无法制作处理器向量。通常,闭包都有不同的、不可命名的类型。相反,您想要的是特征对象,它允许您动态调度回调调用。由于这些不是Sized,您可能希望将它们放入Box. 最终的类型是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()
    }
}
Run Code Online (Sandbox Code Playgroud)

操场


但是,如果您确实这样做,则生命周期可能会出现一些问题(因为您要么强制移入所有内容,要么只修改生命周期的值'static,这不是很方便。相反,以下可能会更好

#[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);
}
Run Code Online (Sandbox Code Playgroud)

操场

只需注意以下几点:

  1. 您必须手动获取drop生产者,否则 Rust 会抱怨它将在作用域末尾被删除,但它包含(通过闭包)对 的独占引用modified,这是不行的,因为我尝试读取它。
  2. 当前,run_callbacks取 a &mut self,因为我们只需要 a FnMut。如果您希望它只是 a &self,则需要替换FnMutFn,这意味着回调仍然可以修改它们外部的内容,但不能修改内部的内容。