enum を宣言する
enum は、値が固定された名前付きバリアントのセットである型を宣言します。
enum Status {
ready,
failed,
}
これで Status はちょうど 2 つの値、Status.ready と Status.failed を持つ型になります。それ以外は Status になれません。
文法は意図的に小さく作られています。
enum Name {で宣言を開く。- 各行に 1 つのバリアント名をカンマ区切りで並べる。
}で閉じる。
ペイロードなし、判別子の値なし、派生メソッドなし——これが enum を「小さな直和型」のままに保ちます。
enum を使う
バリアントは enum 型で修飾して名指します。
let state: Status = Status.ready
右辺が型を確定させれば : Status の注釈は任意です。たいていの場合は、
let state = Status.ready
と書けば、コンパイラーは型を Status と推論します。
enum の値を比較する
2 つの enum 値は、同じバリアントなら等しいです。
if state == Status.ready {
check world.out.write("ready\n")
} else {
check world.out.write("not ready\n")
}
enum で分岐する最もシンプルな方法です。網羅的な解析——すべての バリアントを明示的に扱う——には match に手を伸ばします。
match state {
.ready => { check world.out.write("ready\n") }
.failed => { check world.out.write("failed\n") }
}
if/else if に対する match の利点は、後で 3 つ目のバリアントを追加したときに現れます。新しいケースが欠けているすべての match をコンパイラーが教えてくれます——if/else の連鎖は、デフォルト分岐に静かに流れます。
Choice と match で match をより詳しく扱います。enum と choice の両方で動きます。
動作例
公式の Zero サンプルは、enum と choice を同じファイルに並べています。
Status はこのスニペットでは何もしていません——対比を見せるためにあります。choice のバリアントはマッチ時にペイロード(value、message)を束縛します。enum のバリアントは束縛するものがない——なぜなら束縛すべきものがないから——ので、何も束縛しません。
enum と choice: 簡単な判断ツリー
短いルール。
- バリアントが 単なるラベル →
enum。 - バリアントが データを持たねばならない →
choice。
ライフサイクル状態をモデル化していて、最終的に「失敗」状態にエラーメッセージを付ける必要が出たら、型を enum から choice に切り替えます。バリアントはそれぞれペイロード型を得て、下流の match アームはそのペイロードのバインディングを得ます。コンパイラーが導いてくれるリファクタリングです。
具体的には、
// 変更前——enum、ペイロードなし
enum Status {
ready,
failed,
}
// 変更後——各バリアントにペイロードを持つ choice
choice Status {
ready: Void,
failed: String,
}
ペイロードが Void のバリアントは、choice 形式での単なるラベルです。同じ 論理的 な状態に対して enum と choice の両方を使えますが、データを付ける必要が本当にないときは enum を選びましょう。
用途
enum が正しい答えになる、日常的な例です。
- メタデータなしのライフサイクル。
Loading、Ready、Empty——純粋な状態、ペイロードなし。 - モード。 ファイルオープンモードの
Read、Write、Append。 - 方向。
North、South、East、West。 - ログレベル。
Trace、Debug、Info、Warn、Error。(後でメッセージを追加したくなったら、その時点でchoiceに切り替えます。) - 曜日。 定番の例。
マジックな整数定数(0 = pending、1 = active、2 = done)に手を伸ばしそうな場面はほぼ常に、enum のほうがクリアです。
スタイルメモ
- 小文字のバリアント名は、言語の他の場所の識別子に対する Zero のスタイルと一致します。
- 最後のバリアントの後ろの末尾カンマは構いません(むしろ diff フレンドリーのために推奨——新しいバリアントを追加しても前の行が変わりません)。
- enum のリストは小さく保つ。1 ダースのバリアントがあって多くにペイロードを持たせたいなら、より大きな
enumではなくchoice——あるいは設計の見直し——を検討する時です。
次回: Choice と Match
自然な次の一歩は、よりリッチな従兄弟です——choice と match ——Zero のタグ付き共用体型と、それに伴うパターンマッチ構文です。
よくある質問
Zero の enum とは?
enum は、値が固定された名前付きバリアントのいずれかである型を宣言します——追加のペイロードを持たないラベル。例: enum Status { ready, failed }。Status 型の値はちょうど Status.ready または Status.failed のどちらかで、コンパイラーがそれを強制します。
enum は choice とどう違いますか?
enum のバリアントはプレーンなラベルです——データを持ちません。choice はタグ付き共用体で、各バリアントは関連するペイロード型を持ちます。例: choice Result { ok: i32, err: String }。名前でケースを区別するだけなら enum、各ケースに追加情報を持たせるなら choice を使います。
enum 値のバリアントはどう調べますか?
値をバリアントと比較します: if status == Status.ready { ... }。すべてのバリアントにわたる網羅的な分岐には match を使います——バリアントを見逃すとコンパイラーが警告してくれるので、値が直和型のときは if/else if 連鎖より match を選ぶ主な理由です。
Zero の enum バリアントは関連値を持てますか?
いいえ——それが choice の役割です。enum は意図的に最小の直和型で、各バリアントは単なるラベルです。バリアントに i32 や String を関連付ける必要があるなら、enum を卒業して choice に移る時です。
Zero ではいつ enum を使うべきですか?
値が小さく名前付きの状態セットのちょうどひとつで、その状態が追加データを持たない場合に enum を使います。例: 曜日、信号の色、メタデータのないライフサイクル状態、ログレベル。バリアントにデータを付けたくなったら、choice に切り替える時です。