包含数据的通用回调

generic callback with data

提问人:Makogan 提问时间:8/24/2022 更新时间:8/24/2022 访问量:56

问:

关于这个话题已经有一个非常流行的问题,但我不;T完全理解答案。

目标是:

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

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

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

说我有

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

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

事件 rust 回调

评论


答:

5赞 jthulhu 8/24/2022 #1

事实上,你不能做一个处理器的向量。通常,闭包都有不同的、不可命名的类型。相反,您需要的是 trait 对象,它允许您动态调度回调调用。由于这些不是 ,您可能希望将它们放在 .最后一种类型是 。SizedBoxVec<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()
    }
}

参观游乐场


但是,如果您这样做,您可能会在生命周期方面遇到一些问题(因为您要么强制移入所有内容,要么只修改生命周期的值,这不是很方便。相反,以下方法可能会更好'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);
}

参观游乐场

需要注意的几点:

  1. 您必须手动向生产者提交,否则 Rust 会抱怨它会在范围的末尾被删除,但它包含(通过闭包)对 的独占引用,这是不行的,因为我尝试阅读它。dropmodified
  2. 当前,取一个 ,因为我们只需要一个 .如果你希望它只是一个 ,你需要替换为 ,这意味着回调仍然可以修改它们外部的内容,但不能修改内部的内容。run_callbacks&mut selfFnMut&selfFnMutFn

评论

0赞 Makogan 8/24/2022
为了完整起见,您将如何称呼这些对象为循环?
0赞 jthulhu 8/24/2022
@Makogan我也添加了一个示例。
2赞 Bamontan 8/24/2022 #2

是的,所有的闭包都是不同的类型,所以如果你想拥有不同闭包的 vec,你需要使它们成为特征对象。这可以是 archieve(或任何智能指针)。 实现,因此您可以拥有并可以对它们进行 vec,并调用它们的回调: playgroundBox<dyn Trait>Box<dyn FnMut()>FnMut()Processor<Box<dyn FnMut()>>