錆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)
}
錆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.16.0によって生成されたLLVM IR inを見てみましょう。 Rust Playgroundこの簡単お見せすることができます
define internal fastcc zeroext i1 @_ZN10playground10variant_eq17h3a88b3837dfe66d4E(i8 %.0.0.val, i8 %.0.0.val1) unnamed_addr #0 {
entry-block:
%switch2 = icmp eq i8 %.0.0.val, 1
%switch = icmp ne i8 %.0.0.val1, 1
br i1 %switch2, label %bb5, label %bb4
bb3: ; preds = %bb5, %bb4
br label %bb6
bb4: ; preds = %entry-block
br i1 %switch, label %bb6, label %bb3
bb5: ; preds = %entry-block
br i1 %switch, label %bb3, label %bb6
bb6: ; preds = %bb5, %bb4, %bb3
%_0.0 = phi i1 [ false, %bb3 ], [ true, %bb4 ], [ true, %bb5 ]
ret i1 %_0.0
}
をショートバージョンが、その後、他の列挙型変異体と比較し、我々は1つの列挙型のバリアントのスイッチを行うことです。それは全体的にかなり効率的ですが、私は変種数を直接比較するだけではないことに驚いています。おそらく、これは最適化パスが処理できるものでしょうか?
マクロを作成して関数を生成したい場合は、このようなものが良いスタートになるかもしれません。
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));
}
マクロには制限がありますが、すべてのバリアントには1つのバリアントが必要です。サポートするバリアント、複数のバリアント、構造バリアント、可視性などはすべて本物のハードです。おそらく手続き型マクロはそれを少し楽にするでしょう。
質問には関係ありませんが、「追加(追加)」という構文の意味を教えてください。最初の「追加」とは何ですか?また、2番目のものは何ですか? –
@mose:最初の 'Add'は列挙型の名前です。 2番目はそのバリアントの型であり、 'Add'という名前の別の型(' struct'や別の 'enum'、おそらくは型別名)の存在を前提としています。バリアントの名前は、バリアントのタイプの名前と同じである必要はないことに注意してください。これは、OPが名前を付けた方法です。 –