- この記事は、CyberAgent エンジニア Advent Calendar 2014の7日目の記事です。
- 6日目はkamatama41さんのアメーバピグへのGoogle BigQuery導入までのもろもろ設定記 です
- 8日目はstormcat24さんです
はじめに
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 { // Make Enum from Langs return goenum.EnumerateStruct(&Langs) }
以下は実際に使ってみた例。
func main() { var langs goenum.Enum = Langs.Enum() // 名前の列挙 fmt.Println(langs.Names()) // --> [Go Python Ruby Haskel] // 値の列挙 fmt.Println(langs.Values()) // --> [1 2 3 4] // 値 -> 名前の取得 fmt.Println(langs.MustName(1)) // --> Go // 名前 -> 値の取得 fmt.Println(langs.MustValue("Python")) // --> 2 }
本当は構造体で元の列挙値を定義するやり方にはしたくなかったんだけど、自分のGo力が低すぎるのでこういう仕上がりになっているので、もっと良いやり方があったらぜひ教えて欲しいところです。
追記
Big Sky :: Re: GoLangでJavaのenumっぽいライブラリ作った話でstringerというコマンドを使う方法を教えてもらいました。1.4では go generate コマンドもあるし、これ使ってgenerateするのがIdiomatic wayのような気がしますね。mattnさんありがとうございます!