【发布时间】:2021-10-31 19:58:13
【问题描述】:
我有一个枚举,其变体具有相同的结构(这些变体可以转换角色)并且出现在大型数据结构中。
我需要通过交换变体的角色来进行某些计算(一个相当长的程序)。 由于数据量大,我应该避免直接对数据进行这种排列。
为了最大限度地减少代码长度并使未来的演变更加可靠,我决定以通用方式进行编程。
到目前为止,我已经尝试过使用宏。 以下代码是我跨变体进行的通用实现的示例:(playground)
// enum with same variants shapes
#[derive(Debug, Clone)]
enum Choice{
A{ val: f32, },
B{ val: f32, },
}
use Choice::{A,B,};
// code to be implemented
trait MyTrait<const P: usize> { // P is a permutation parameter
fn eval(&self) -> f32;
fn zoom(&mut self);
fn transmute(self) -> Self;
}
// variant permutation macros
macro_rules! choice_a {
(0 | val: $a:ident) => (A{val: $a});
(1 | val: $a:ident) => (B{val: $a});
(0 | val: ref $a:ident) => (A{val: ref $a});
(1 | val: ref $a:ident) => (B{val: ref $a});
(0 | val: ref mut $a:ident) => (A{val: ref mut $a});
(1 | val: ref mut $a:ident) => (B{val: ref mut $a});
(0 | val: $a:expr) => (A{val: $a});
(1 | val: $a:expr) => (B{val: $a});
}
macro_rules! choice_b {
(0 | val: $a:ident) => (B{ val: $a});
(1 | val: $a:ident) => (A{ val: $a});
(0 | val: ref $a:ident) => (B{ val: ref $a});
(1 | val: ref $a:ident) => (A{ val: ref $a});
(0 | val: ref mut $a:ident) => (B{ val: ref mut $a});
(1 | val: ref mut $a:ident) => (A{ val: ref mut $a});
(0 | val: $a:expr) => (B{ val: $a});
(1 | val: $a:expr) => (A{ val: $a});
}
// macro for generic implementation
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} => { *va /= 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} },
}
}
}
);
}
mytrait!(0); // no permutation case
mytrait!(1); // permutation case
fn main() {
for x in vec![Choice::A{ val: 16.0}, Choice::B{ val: 16.0}] {
let mut y = x.clone();
println!("Variants roles are not permuted:");
println!("y = {:?}", y);
println!("eval y = {:?}", MyTrait::<0>::eval(&y));
MyTrait::<0>::zoom(&mut y);
println!("zoomed y = {:?}", y);
let y = MyTrait::<0>::transmute(y);
println!("transmuted zoomed y = {:?}", y);
println!("-------------------------");
println!("Variants roles are permuted:");
let mut z = x;
println!("z = {:?}", z);
println!("eval z = {:?}", MyTrait::<1>::eval(&z));
MyTrait::<1>::zoom(&mut z);
println!("zoomed z = {:?}", z);
let z = MyTrait::<1>::transmute(z);
println!("transmuted zoomed z = {:?}", z);
println!("=====================================");
}
}
结果:
Variants roles are not permuted:
y = A { val: 16.0 }
eval y = 256.0
zoomed y = A { val: 32.0 }
transmuted zoomed y = B { val: 33.0 }
-------------------------
Variants roles are permuted:
z = A { val: 16.0 }
eval z = 4.0
zoomed z = A { val: 8.0 }
transmuted zoomed z = B { val: 7.0 }
=====================================
Variants roles are not permuted:
y = B { val: 16.0 }
eval y = 4.0
zoomed y = B { val: 8.0 }
transmuted zoomed y = A { val: 7.0 }
-------------------------
Variants roles are permuted:
z = B { val: 16.0 }
eval z = 256.0
zoomed z = B { val: 32.0 }
transmuted zoomed z = A { val: 33.0 }
=====================================
我的代码有效,但并不完全令人满意。 确实,我的 choice_a! 和 choice_b! 宏不是进化的,它们的使用是僵化的,并且会产生整个模式或枚举的完整定义。 如果这些宏只产生枚举的名称会更好。 例如,最好能写成 choice_a!($p) { val: va - 1.0} 而不是 choice_a!{$p | val: va - 1.0},最好能写成 choice_b!($p){ val: ref mut va} 而不是 choice_b!{$ p | val: ref mut va}.
可以这样做吗? 除了宏还有其他方法可以跨变体进行这种通用实现吗?
【问题讨论】:
标签: generics rust enums implementation variant