LoginSignup
43
22

More than 3 years have passed since last update.

GoでEnumを定義する 〜intやstringよりも厳密に〜

Last updated at Posted at 2019-01-04

よくあるやつ

simpleSeason.go
package enum

type SimpleSeason int

const (
    _ SimpleSeason = iota
    Spring
    Summer
    Autumn
    Winter
)

var simpleSeasonStrings = [4]string{"未定義", "春", "夏", "秋", "冬"}

func (s SimpleSeason) String() string {
    return simpleSeasonStrings[s]
}
main.go
package main

import (
    "fmt"

    "github.com/ikngtty/playground/enum"
)

func main() {
    fmt.Println(enum.Summer) // -> 夏
}

問題点

main.go
func printSeason(s enum.SimpleSeason) {
    fmt.Println(s)
}

func main() {
    // enum.Autumnじゃなくても無理やりautumnを作れてしまう
    var autumn enum.SimpleSeason = 3

    // 0〜4以外のありえない値を入れられる
    printSeason(64) // runtime error: index out of range
}
simpleSeason.go
var simpleSeasonStrings = [5]string{"未定義", "春", "夏", "秋", "冬"}

func (s SimpleSeason) String() string {
    // なので、SimpleSeasonを扱うときは必ずありえない値が来る可能性を
    // 考慮しないといけない。
    if s < 0 || s > 4 {
        return "お前そういうことすんのやめろよマジで。"
    }

    return simpleSeasonStrings[s]
}

ぼくの考えたすごいやつ

season.go
package enum

type Season struct{ value string }

var Spring = Season{"春"}
var Summer = Season{"夏"}
var Autumn = Season{"秋"}
var Winter = Season{"冬"}

// String()をわざわざ定義しなくても {春} とか出るので十分わかりやすい
func (s Season) String() string {
    if s.value == "" {
        return "未定義"
    }
    return s.value
}
main.go
package main

import (
    "fmt"

    "github.com/ikngtty/playground/enum"
)

func main() {
    fmt.Println(enum.Winter) // -> 冬

    // パッケージの外からは、enum.Spring〜enum.Winterを使わない限り、
    // ゼロ値しか作ることができない。
    mySeason := enum.Season{}
    fmt.Println(mySeason) // -> 未定義
}

フィールドを小文字にしてアクセス制限するのがポイント。
欠点を見つけた方は教えてください。

おまけ:列挙値の変数名が被る件

enum毎にいちいちpackage切りたくない時。
構造体を名前空間代わりにできそうです。

variousEnums.go
package enum

type ProgrammingLang struct{ value string }

var ProgrammingLangs = struct {
    Perl   ProgrammingLang
    Ruby   ProgrammingLang
    Python ProgrammingLang
}{
    Perl:   ProgrammingLang{"Perl"},
    Ruby:   ProgrammingLang{"Ruby"},
    Python: ProgrammingLang{"Python"},
}

type Jewelry struct{ value string }

var Jewelries = struct {
    Perl   Jewelry
    Ruby   Jewelry
    Garnet Jewelry
}{
    Perl:   Jewelry{"Perl"},
    Ruby:   Jewelry{"Ruby"},
    Garnet: Jewelry{"Garnet"},
}
main.go
package main

import (
    "fmt"

    "github.com/ikngtty/playground/enum"
)

func main() {
    fmt.Println(enum.ProgrammingLangs.Ruby) // -> {Ruby}
    fmt.Println(enum.Jewelries.Ruby)        // -> {Ruby}
}
43
22
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
43
22