You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust中创建可变回调列表的困惑:参考SO热门问题仍存疑

Rust 回调列表实现方案

你说的完全没错:Processor<CB> 因为泛型参数 CB 的存在,每用不同的回调类型(比如不同捕获逻辑的闭包、不同签名的函数)实例化出来的 Processor 都是完全独立的类型,确实没法直接放进同一个 Vec 里。下面给你讲清楚怎么解决这个问题,实现能存储可变回调的列表:

一、用 trait 对象统一类型

要把不同类型的回调装进同一个容器,核心思路是用动态分发的 trait 对象做类型擦除。

1. 定义回调 trait

先定义一个描述回调行为的 trait:

trait KeyCallback {
    fn trigger(&mut self);
}

2. 给所有 FnMut() 类型实现 trait

让任何符合 FnMut() 签名的闭包、函数都能适配这个 trait:

impl<F> KeyCallback for F
where
    F: FnMut(),
{
    fn trigger(&mut self) {
        self(); // 直接调用闭包/函数
    }
}

3. 存储 trait 对象到 Vec

现在不管是什么类型的回调,都可以转换成 Box<dyn KeyCallback> 这个统一类型,放进 Vec:

fn main() {
    // 示例1:修改外部变量的闭包
    let mut click_count = 0;
    let mut click_cb = move || {
        click_count += 1;
        println!("点击次数: {}", click_count);
    };

    // 示例2:修改另一个外部数据的闭包
    let mut username = String::from("guest");
    let mut login_cb = move || {
        username.replace_range(.., "admin");
        println!("已切换用户: {}", username);
    };

    // 创建回调列表
    let mut callbacks: Vec<Box<dyn KeyCallback>> = Vec::new();
    callbacks.push(Box::new(click_cb));
    callbacks.push(Box::new(login_cb));

    // 模拟按键触发,遍历调用所有回调
    for cb in callbacks.iter_mut() {
        cb.trigger();
    }
}

二、更简洁的方案:直接用 FnMut() trait 对象

其实不用额外定义 KeyCallback,直接用 Box<dyn FnMut()> 也能实现,因为 FnMut 本身就是 Rust 内置的 trait:

fn main() {
    let mut count = 0;
    let mut cb1 = move || count += 1;
    let mut cb2 = move || println!("count 当前值: {}", count);

    let mut callbacks: Vec<Box<dyn FnMut()>> = Vec::new();
    callbacks.push(Box::new(cb1));
    callbacks.push(Box::new(cb2));

    // 触发所有回调
    for cb in callbacks.iter_mut() {
        cb();
    }
}

三、处理共享数据的回调

如果多个回调需要修改同一份外部数据,得用内部可变性容器:

  • 单线程场景用 Rc<RefCell<T>>
  • 多线程场景用 Arc<Mutex<T>>

示例(单线程共享数据):

use std::rc::Rc;
use std::cell::RefCell;

fn main() {
    let shared_count = Rc::new(RefCell::new(0));

    // 回调1:增加计数
    let count_inc = {
        let count = Rc::clone(&shared_count);
        move || *count.borrow_mut() += 1
    };

    // 回调2:打印计数
    let count_print = {
        let count = Rc::clone(&shared_count);
        move || println!("当前计数: {}", *count.borrow())
    };

    let mut callbacks: Vec<Box<dyn FnMut()>> = Vec::new();
    callbacks.push(Box::new(count_inc));
    callbacks.push(Box::new(count_print));

    // 触发两次
    for _ in 0..2 {
        for cb in callbacks.iter_mut() {
            cb();
        }
    }
}

内容的提问来源于stack exchange,提问作者Makogan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 18:35:23