【Go】Enumを扱う
Enum(列挙型)とは
列挙型とは、プログラミング言語やデータベース管理システムなどにおけるデータ型の一つで、複数の異なる定数を一つの集合として定義するもの。多くの言語では “enum” の略号で示される。 列挙型(集合型)とは - 意味をわかりやすく - IT用語辞典 e-Words
とあるように、複数の定数を1つのクラス(型)としてまとめて管理できるもの。
Javaであれば
public enum Fruits{ ORANGE, APPLE, BANANA };
のように書けるみたい。 引数や戻り値の型をEnumに限定できるのでプログラムの堅牢性が増すし、可読性が上がったりする。
GoでEnumを扱うには?
GoにはJavaにおけるEnum(列挙型)のような機能が無い。
そのため、const定義とその中に iota (※1)を使うことでEnumを扱うのが一般的?かもしれない。
ただし、Goは変数を初期化する際に明示的に値を代入しない場合に、デフォルトで割り振られる値が決まっているので注意。
これをZero Value(ゼロ値)(※2)といい、int型の場合は0
で、string型の場合は“”
で初期化されるようになっている。
※1: iotaとは定数宣言(const)内で使用される識別子のことで、0から始まる型なしの整数連番を生成してくれる。
※2: Zero Value(ゼロ値)については、ゼロ値を使おう #golang - Qiitaが分かりやすい。
この仕様から、Goでは以下のように定数の割り当てを1からにすればいい。
type Fruit int const ( ORANGE Fruit = iota + 1 // 1 APPLE // 2 BANANA // 3 )
以下のfruits := Fruits{}
は明示的に値を代入していないので、Zero Valueの仕様により、fruits.F
にはint型のデフォルト値の0が入ってしまう。
そのため iota + 1
としておけば、意図した挙動になる。
type Fruits struct { F Fruit } func main() { fruits := Fruits{} // 変数初期化時に値を代入していないので、0が割り振られる switch fruits.F { case ORANGE: fmt.Println(“オレンジ”) // (+1していないと、この分岐を通ってしまう) case APPLE: fmt.Println(“りんご”) case BANANA: fmt.Println(“バナナ”) default: fmt.Println(“エラー”) // この分岐を通ってくれる } }
公式パッケージの例
Goの標準パッケージtime
に用意されている構造体time.Time型
を返すtime.Date
関数は第2引数にtime.Month
型を求めており、
func Date(year int, month Month, day int, hour int, min int, sec int, nsec int, loc *Location) Time
以下のようにtime.Month
型はconst定義と iota を使って表現されており、これによって引数の型をEnumであるtime.Month
に限定しようとしていることが分かる(intをベタ書きしてもコンパイル通るとは思うけど)。
(time/time.go) // A Month specifies a month of the year (January = 1, ...). type Month int const ( January Month = 1 + iota February March April May June July August September October November December )
実際使う場合はこんな感じ。
time.Date(2022, time.August, 10, 12, 0, 0, 0, time.UTC)