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

如何编写宏自动生成EnumDispatch枚举的Match匹配块

如何编写宏自动生成EnumDispatch枚举的Match匹配块

我目前用EnumDispatch来封装所有要执行的动作,每个动作都是需要接收参数的结构体。现有代码大概是这样的:

#[enum_dispatch]
pub trait ActionRunner {
    fn run(&self);
}

#[enum_dispatch(ActionRunner)]
#[derive(Debug)]
enum ActionsEnum {
    MyAction,
    MyAction2,
}

现在我需要根据字符串匹配,把JSON参数反序列化成对应的Action结构体,再转换成ActionsEnum枚举。手动写的话是下面这样:

let action: ActionsEnum = match my_output.action.name.as_str() {
    "MyAction" => {
        let foo: MyAction = serde_json::from_value(args).unwrap();
        foo.into()
    },
    "MyAction2" => {
        let foo: MyAction2 = serde_json::from_value(args).unwrap();
        foo.into()
    },
    _ => panic!("No found action"),
};

但每次新增一个Action,都要手动加一个match分支,实在太繁琐了。我想写个宏来自动生成这段匹配逻辑,但卡在怎么正确传递参数上,感觉离成功不远了但就是摸不准门道。


其实我们可以写一个宏,让它帮我们自动生成所有匹配分支,不用手动重复写反序列化和转换的代码。下面是可行的实现:

#[macro_export]
macro_rules! match_all_variants {
    ($input_str:expr, $action_args:expr, $($variant:ty),+) => {
        match $input_str {
            $(
                // 把类型名转成字符串,和动作名称匹配
                stringify!($variant) => {
                    // 克隆参数避免所有权问题,根据你的场景也可以用引用
                    let action: $variant = serde_json::from_value($action_args.clone()).unwrap();
                    action.into()
                },
            )*
            _ => panic!("No found action: {}", $input_str),
        }
    };
}

使用的时候就简单多了,只需要把所有Action结构体传进去就行:

let action: ActionsEnum = match_all_variants!(
    my_output.action.name.as_str(),
    args,
    MyAction, MyAction2
);

宏的工作逻辑解释

  • $($variant:ty),+:这个模式用来接收一个或多个Action结构体类型,用逗号分隔
  • stringify!($variant):Rust内置宏,会把传入的类型名转换成对应的字符串,刚好和我们的动作名称字符串对应
  • 每个分支里的逻辑和手动写的完全一致:反序列化JSON参数、把结构体转换成ActionsEnum枚举
  • 最后统一处理未知动作的情况,还把错误信息改成了带具体输入字符串的版本,排查问题更方便

如果后续新增了MyAction3,只需要在宏调用里加上MyAction3就行,不用再手动写整个match分支啦。

备注:内容来源于stack exchange,提问作者Bruno Murino

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.16 07:00:28