はじめに
Goでは言語標準でenumという機構がなくてつらかったのでそれっぽいものを作ったよという話。
Goではiotaを使って以下のようにして定数を列挙することはできる。
const (
Go int = iota + 1
Python
Ruby
)
ただし、このやり方だとJavaのenumのように
- 値から名前(↑でいう"Java"という定数名)を取得したり、名前からその値を引く
- 全ての値を列挙する
- 全ての名前を列挙する
というようなことができない。下記のように値→名前を取得する関数を定義することはできるので1.はなんとかなるわけなんだけど、これを毎回書くのは面倒なので goenum というライブラリを作ってみたよという話(ここからが本題)。
http://play.golang.org/p/5nrPOMEQKZ
package main
import (
"fmt"
)
type Lang int
func (l Lang) String() string {
switch l {
case Go:
return "Go"
case Python:
return "Python"
case Ruby:
return "Ruby"
}
panic("Unknown value")
}
const (
Go Lang = iota + 1
Python
Ruby
)
func main() {
fmt.Printf("%s: %d\n", Go.String(), Go)
fmt.Printf("%s: %d\n", Python.String(), Python)
fmt.Printf("%s: %d\n", Ruby.String(), Ruby)
}
goenum
goenumでは列挙する値はconstではなくstructを使って定義する。
type LangsEnum struct {
Go int
Python int
Ruby int
Haskel int
}
でで、その構造体(↑でいうLangsEnum)に下記のようなEnum()というメソッドを定義しておき、goenum.EnumerateStruct という関数にLangsEnumの実体を渡す。goenum.EnumerateStruct() という関数は goenum.Enum を返すんだけど、これが
の変換を行ってくれる。
var Langs LangsEnum = LangsEnum{1, 2, 3, 4}
func (e LangsEnum) Enum() goenum.Enum {
return goenum.EnumerateStruct(&Langs)
}
以下は実際に使ってみた例。
func main() {
var langs goenum.Enum = Langs.Enum()
fmt.Println(langs.Names())
fmt.Println(langs.Values())
fmt.Println(langs.MustName(1))
fmt.Println(langs.MustValue("Python"))
}
本当は構造体で元の列挙値を定義するやり方にはしたくなかったんだけど、自分のGo力が低すぎるのでこういう仕上がりになっているので、もっと良いやり方があったらぜひ教えて欲しいところです。
追記
Big Sky :: Re: GoLangでJavaのenumっぽいライブラリ作った話でstringerというコマンドを使う方法を教えてもらいました。1.4では go generate コマンドもあるし、これ使ってgenerateするのがIdiomatic wayのような気がしますね。mattnさんありがとうございます!