Posted at
Go2Day 8

Go で Enum を定義するときのちょっとした気遣い

@mosaxiv さんの代打で、Go で Enum を定義するときに気になっていたこととその解決策についてお話します。

Go にはデフォルトで Enum を定義する仕組みがないため、一般的に const 宣言内で iota を使って次のように定義することが多いと思います。

type Type int

const (
a Type = iota
b
c
)

しかし、Go には Zero-Value といって変数を初期化する際に明示的に値を代入しない場合、デフォルトで割り当てられる値が決まっています。例えば int 型の変数の場合「0」です。

すると iota は「0」から始まる連続する整数を生成する識別子であるため、上で宣言した a がデフォルト値でもない限り、変数を初期化する際に明示的に値を指定していないにも関わらずデフォルト値でもない a で勝手に初期化されてしまっている状態が発生します。

文章では少し伝わりにくいと思うのでコードで示すと

type S struct {

T Type
}

func main() {
s := S{}
switch s.T {
case a:
fmt.Println("a") // <- 実際にはここを通る
case b:
fmt.Println("b")
case c:
fmt.Println("b")
default:
fmt.Println("default") // <- s.T に何も設定していなかったらここを通ってほしい
}
}

という具合です。

この問題は iota を「1」から始めることで解決できます。このちょっとしたおせっかい気遣いによって次のように意図した処理が行われます。

const (

a Type = iota + 1 // <- 1から始める
b
c
)

func main() {
s := S{}
switch s.T {
case a:
fmt.Println("a")
case b:
fmt.Println("b")
case c:
fmt.Println("b")
default:
fmt.Println("default") // <- s.T に何も設定していないのでここを通る!
}
}

ただこの解決策がベストかどうか分からないので、もう少しいい案があるよ!という方はコメント欄までお願いします:bow: