如何编写宏自动生成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
相关产品推荐
相关产品推荐

