ことのあらまし
Rust入門してもうすぐ2週間のみゆきです。
Rustの列挙型は集合論では直和集合、Tagged-Union と言われたり、列挙子に値を持てる点がJavaやC#といった他の言語の列挙型と違う点と解説されています。
なんとなく雰囲気が違うのは感じ取れるのですが、私の頭では理解が追い付いてないです。
特に、RustのEnumが値を持てる点で、「そういえばJavaのenumも高機能でどんな値でも(自作型でも配列でも)持つことができたはず!」ということを思い出しました。
では、両者は一体何が違うのか実際に書いて比較してみました。
※勢いで書いているので間違ったことを書いているかもしれません ><
Rust と Java の Enum を比較
まず、確認の意味も含めて単純なenumでJavaとRustを比較してみます。
enum Hoge {
V1,
V2,
}
// 使い方
let hoge: Hoge = Hoge::V1;
enum Hoge {
V1, //ordinal() = 0
V2, //ordinal() = 1
}
// 使い方
Hoge hoge = Hoge.V1;
見た目はほとんど変わらないですね!
Java の方は列挙子の上の方から暗黙的に整数0から順番に割り振られます。
Rust の方も整数が割り振られるのでしょうか?その辺はまだわかりません。
ちなみに、Javaの方はそれぞれの列挙子は静的なオブジェクトのようになっています。
以下のようなイメージです。
class Hoge {
public static final V1 = new Hoge(0); // 列挙子の値は静的に値が決まるイメージ
public static final V2 = new Hoge(1);
private final int id;
private Hoge(int id) {
this.id = id;
}
public int ordinal() {
return id;
}
}
Hoge hoge = Hoge.V1; //列挙型とほとんど同じ
つまり、列挙型の列挙子の値は定義時に静的に決まって、別オブジェクトに書き換えることもできず、実行が終わるまで固定の値(定数)というイメージです。
直和集合を考える
直和集合とは、 単純に足し合わせたもの と考えると良さそうです。
AAという集合{ A, B, C } と、
BBという集合 { A, B, D } があったときに、
和集合 CC は { A_V1, B_V1, C_V1, A_V2, B_V2, D_V2 } の6個の区別された集合(3個 + 3個)になるらしいです。
(複雑に見えますが _V1, _V2 という添え字をつけてるだけです)
足し合わせる前に同じ要素(AやB)があっても同じものとはみなさない(区別する)のが特徴ですね。
(ちなみに、和集合(AA ∪ BB) は被ったところは区別しないです。なので4個の集合{A, B, C, D} です)
enum AA { A, B, C }
enum BB { A, B, D }
enum CC {
V1(AA),//3通り
V2(BB),//3通り
}
// 列挙型 CC は6通りの値を表す。
let cc: CC = CC::V1(AA::A);
//使用時にAA::Aを渡してインスタンス生成している。
なるほどなるほど。では、Java では表せないのでしょうか?
enum AA { A, B, C }
enum BB { A, B, D }
enum CC {
V1(/* 静的に値が決まってしまう...*/),
V2(/* 静的に値が決まってしまう...*/),
}
//CC cc = CC.V1(AA.A); // こういうのがやりたかったけどできない!
//定義時点で値が決まってしまうので普通にはできなかった!
Java は上に書いた通りenumの値は定義時に静的に決まってしまうのですね。
(定義時にコンストラクトされちゃうから、何かしらの値を渡さないとダメ…!)
一方、Rustは列挙型は値が動的に決まる…っぽい。なるほど!
そして、列挙型なのにインスタンスを生成してるんですね。
比較してみるとよくわかります。
ただし
enum CC {
V1(),
V2();
private AA v1;
private BB v2;
private CC(){}
private CC(AA aa) {
v1 = aa;
}
private CC(BB bb) {
v2 = bb;
}
public CC set(AA v) {
v1 = v;
return this;
}
public CC set(BB v) {
v2 = v;
return this;
}
}
CC cc = CC.V1.set(AA.A);
// こうすれば動的に値を変更できる。
Java の enum はシングルトンのようなもので、各列挙子は静的に生成されたオブジェクトなので値を動的に変更しようと思えばできます。(Javaで列挙子の値を動的に書き変えるのはよくないらしいです)
しかし、書き換えると静的なオブジェクトなので全てのCC.V1の値が変わってしまうので Rustのようにはいかないです。
参考ページ