从Rust 1.21.0版本开始,你可以使用std::mem::discriminant
函数:
fn variant_eq(a: &Op, b: &Op) -> bool {
std::mem::discriminant(a) == std::mem::discriminant(b)
}
这很好,因为它可以非常通用:
fn variant_eq<T>(a: &T, b: &T) -> bool {
std::mem::discriminant(a) == std::mem::discriminant(b)
}
在 Rust 1.21.0 之前,我会匹配两个参数的元组,并使用 _
或 ..
忽略元组的内容:
struct Add(u8);
struct Sub(u8);
enum Op {
Add(Add),
Sub(Sub),
}
fn variant_eq(a: &Op, b: &Op) -> bool {
match (a, b) {
(&Op::Add(..), &Op::Add(..)) => true,
(&Op::Sub(..), &Op::Sub(..)) => true,
_ => false,
}
}
fn main() {
let a = Op::Add(Add(42));
let b = Op::Add(Add(42));
let c = Op::Add(Add(21));
let d = Op::Sub(Sub(42));
println!("{}", variant_eq(&a, &b));
println!("{}", variant_eq(&a, &c));
println!("{}", variant_eq(&a, &d));
}
虽然组成枚举类型的部分被称为变体,实际上你正在测试它们是否相等,而不是比较它们(这通常用于排序)。因此,我冒昧地给函数改了个名字。
就性能而言,让我们看看在Rust 1.60.0中,在发布模式下生成的LLVM IR(并将variant_eq
标记为#[inline(never)]
)。你可以在Rust Playground中查看:
; playground::variant_eq
; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind nonlazybind readonly uwtable willreturn
define internal fastcc noundef zeroext i1 @_ZN10playground10variant_eq17hc64d59c7864eb861E(i8 %a.0.0.val, i8 %b.0.0.val) unnamed_addr #2 {
start:
%_8.not = icmp eq i8 %a.0.0.val, %b.0.0.val
ret i1 %_8.not
}
这段代码直接比较变量的判别式。
如果你想要一个生成该函数的宏,可以尝试类似下面的代码作为起点。
struct Add(u8);
struct Sub(u8);
macro_rules! foo {
(enum $name:ident {
$($vname:ident($inner:ty),)*
}) => {
enum $name {
$($vname($inner),)*
}
impl $name {
fn variant_eq(&self, b: &Self) -> bool {
match (self, b) {
$((&$name::$vname(..), &$name::$vname(..)) => true,)*
_ => false,
}
}
}
}
}
foo! {
enum Op {
Add(Add),
Sub(Sub),
}
}
fn main() {
let a = Op::Add(Add(42));
let b = Op::Add(Add(42));
let c = Op::Add(Add(21));
let d = Op::Sub(Sub(42));
println!("{}", Op::variant_eq(&a, &b));
println!("{}", Op::variant_eq(&a, &c));
println!("{}", Op::variant_eq(&a, &d));
}
但是,宏确实存在一些限制 - 所有变体都需要具有单个变体。支持单位变体、具有多种类型的变体、结构变体、可见性等都非常困难。也许过程宏会使它稍微容易一些。
Add(Add)
语法的含义吗?第一个Add
是什么,第二个又是什么? - Martin ThomaAdd
是枚举变量的名称。第二个是该变量的类型,它假定存在另一个类型(一个struct
或另一个enum
,或者可能是一个类型别名)名为Add
,其定义未在此处显示。请注意,变量的名称不需要与其类型的名称相同,这只是 OP 选择的命名方式。 - Benjamin Lindley