Rust枚举变体跨实现泛型化及宏方案优化可行性问询
问题解答
1. 你想要的宏写法完全可以实现
只需要调整宏的匹配规则,将排列参数和后续的结构体字段部分分开捕捉即可,示例实现如下:
macro_rules! choice_a { (0 $($fields:tt)*) => { A $($fields)* }; (1 $($fields:tt)*) => { B $($fields)* }; } macro_rules! choice_b { (0 $($fields:tt)*) => { B $($fields)* }; (1 $($fields:tt)*) => { A $($fields)* }; }
调整后你就可以直接用你想要的写法了:
- 构造实例:
choice_a!($p) { val: va - 1.0 } - 匹配模式:
choice_a!($p) { val: ref mut va }
这种写法扩展性极强,后续如果枚举变体新增字段,完全不需要修改宏定义,直接传入对应字段即可。
对应的trait实现部分可以简化为:
macro_rules! mytrait { ($p:tt) => ( impl MyTrait<$p> for Choice { fn eval(&self) -> f32 { match *self { choice_a!($p { val: ref va }) => va.powi(2), choice_b!($p { val: ref va }) => va.sqrt(), } } fn zoom(&mut self) { match *self { choice_a!($p { val: ref mut va }) => *va *= 2.0, choice_b!($p { val: ref mut va }) => *val /= 2.0, } } fn transmute(self) -> Self { match self { choice_a!($p { val: va }) => choice_b!($p { val: va + 1.0 }), choice_b!($p { val: va }) => choice_a!($p { val: va - 1.0 }), } } } ); }
运行效果和你原来的代码完全一致。
2. 除了宏之外的替代实现方案
方案1:基于const判别式的统一匹配
可以不用生成多个trait实现,而是直接在trait方法内通过const参数判断排列规则,逻辑更直观:
impl<const P: usize> MyTrait<P> for Choice { fn eval(&self) -> f32 { let (val, is_role_a) = match self { A { val } => (*val, P == 0), B { val } => (*val, P == 1), }; if is_role_a { val.powi(2) } else { val.sqrt() } } fn zoom(&mut self) { let (val, is_role_a) = match self { A { val } => (val, P == 0), B { val } => (val, P == 1), }; if is_role_a { *val *= 2.0 } else { *val /= 2.0 } } fn transmute(self) -> Self { let (val, is_role_a) = match self { A { val } => (val, P == 0), B { val } => (val, P == 1), }; if is_role_a { choice_b!(P { val: val + 1.0 }) } else { choice_a!(P { val: val - 1.0 }) } } }
这种方案不需要用宏生成多份实现,编译速度更快,逻辑修改也更方便。
方案2:变体逻辑抽离为独立函数
如果你的枚举变体数量更多、排列规则更复杂,可以把不同角色的操作逻辑抽成独立的函数,再根据排列参数选择对应的函数组合,适合逻辑非常复杂的场景。
内容的提问来源于stack exchange,提问作者FreD
相关产品推荐
相关产品推荐

