Goには、Pythonなどの言語にあるような厳密なEnum型が存在しません。
そのため、独自の型定義と定数を組み合わせてEnumを表現するのが一般的です。
今回は、実務でよく使われる2つの表現パターンについて紹介します。
【パターン1】 iota を活用した連番定義
iotaは、定数ブロック内で連番を自動的に割り振るための仕組みです。(Goに備わっている標準機能です)
exmaple.go
type ErrorCode int
const (
BadRequest ErrorCode = iota + 1 // 1
Required // 2
InvalidChars // 3
InternalError // 4
)
iotaの初期値について
Goの整数型のゼロ値は0です。
未初期化の変数と明示的な値を区別するため、iota + 1として1から開始するのが一般的なプラクティスです。
定義したEnum値に対して文字列を返したい場合は、以下のように String() メソッドを定義します。
exmaple.go
func (e ErrorCode) String() string {
switch e {
case BadRequest:
return "BadRequest"
case Required:
return "Required"
case InvalidChars:
return "InvalidChars"
case InternalError:
return "InternalError"
default:
return fmt.Sprintf("ErrorCode(%d)", e)
}
}
これにより、fmt.Printlnなどで出力する際に、数値ではなく意味のある文字列として扱えるようになります。
【パターン2】 mapベースで管理していく
定数で定義した値をキーとして、対応するメッセージをmapに格納する方法です。
フォーマット可能な文字列や複雑なメタデータを関連づけたい場合に有効なパターンになります。
基本的な実装例
type ErrCode int
const (
BadRequest ErrorCode = iota + 1
Required
InvalidChars
InternalError
)
var messageMap = map[ErrorCode]string{
BadRequest: "Bad Request error",
Required: "%sを入力してください",
// ... 続く
}
func (e ErrorCode) Message() string {
if msg, ok := messageMap[ErrCode]; ok {
return msg
}
return "Unknown Error Msg"
}
パラメータをつけた実装例
func (e ErrorCode) FormatMessage(args ...interface{}) string {
template := e.Message()
return fmt.Sprintf(template, args...)
}
msg := Required.FormatMessage("ユーザー名")
// 出力: ユーザー名を入力してください
まとめ
GoでEnum的な構造を実現する際は、要件に応じて使い分けましょう。
-
パターン1
- シンプルで高速な文字列変換が必要な場合
-
パターン2
動的なメッセージ生成や複雑なメタデータ管理が必要な場合
基本的にはパターン1をベースにし、複雑な関連データが必要になった段階でパターン2を検討するのが、Goらしいのかなと思っています。