なぜ楽になるか?
enumで定義した全ての要素をswitch文で考慮しているかlinterがチェックしてくれるから。
具体例
こんなenumを想定する。
type Item uint8
const (
itemA Item = iota
itemB
itemC
)
例えば、アプリケーションで定義している型をDBに入れる値として変換したいときなど、何かしら変換をしたいことがある。すると、以下のような関数を書きがち。
func convertItemToString(i Item) (string, error) {
switch i {
case itemA:
return "itemA", nil
case itemB:
return "itemB", nil
case itemC:
return "itemC", nil
default:
return "", fmt.Errorf("invalid item.")
}
}
ここで、以下のように新たにitemDを追加する。itemDは"itemD"としてマッピングしたいものとする。
const (
itemA Item = iota
itemB
itemC
itemD
)
ただ、うっかりするとconvertItemToStringにitemDのcaseを追加するのを忘れてエラーを起こしてしまう。
そこで、以下のようにあえてdefaultを外して書く。
func convertItemToString(i Item) (string, error) {
switch i {
case itemA:
return "itemA", nil
case itemB:
return "itemB", nil
case itemC:
return "itemC", nil
}
return "", fmt.Errorf("invalid item.")
}
すると、exhaustiveというlinterがitemDのcaseがないことを検知してくれる。ちなみにexhaustiveはgolangci-lintに組み込まれている。
以下のようにlinterを設定
linters:
enable:
- exhaustive
main.go:15:2: missing cases in switch of type main.Item: main.itemD (exhaustive)
switch i {
^
convertItemToStringのような関数でテストを書こうとすると、単調な関数な割には項目が増えるたびにテストをメンテしなければならず面倒になる。しかし、switch文にdefaultを入れないことでlinterがバグの原因を未然に検知してくれるので、対象の関数が十分に簡単であればメンテコストを加味してテストを書かないという選択肢も取れる。
func convertItemToString(i Item) (string, error) {
switch i {
case itemA:
return "itemA", nil
case itemB:
return "itemB", nil
case itemC:
return "itemC", nil
case itemD:
return "itemD", nil
}
return "", fmt.Errorf("invalid item.")
}
ちなみにdefaultで何もしなくても同じようにlintエラーになる
func convertItemToString(i Item) (string, error) {
switch i {
case itemA:
return "itemA", nil
case itemB:
return "itemB", nil
case itemC:
return "itemC", nil
default:
}
return "", fmt.Errorf("invalid item.")
}
main.go:15:2: missing cases in switch of type main.Item: main.itemD (exhaustive)
switch i {