昨日、バグを踏んでしまったので記事にしておきます。アドベントカレンダー空いてたし。
Twitterにも以下のようにアップしています。
#golang で独自型作って値制御するやつやってたらきかなかったので、調べてたら1.10から発生してるtype alias周りのバグらしい。もう直して入るんだけど、リリースされるのは1.12みたい
— Y.Komatsu (@yusuke_k0matsu) December 21, 2018
あぁ〜、バグ踏んだぁ(´・ω・`) https://t.co/BrqbFWB3T9
https://t.co/goaBEtg71L
きちんと説明してみる
例えば以下のような処理があるとします。
package main
import "fmt"
type MyString string
const (
YES MyString = "yes"
NO = "no"
DONTKNOW = "i dont know"
)
func foo(arg MyString) {
fmt.Println(arg)
}
func main() {
foo(YES) //success, prints "yes"
foo("jop") //fails to compile
}
MyString
は独自型です。取りうる値は、YES
, NO
, DONTKNOW
の3種類です。
fooの引数はMyString
なので、YES
は正常に動きますが、jop
はただのstringなのでコンパイルエラーを起こします。
これが正しい処理で、いままでもそのように動いていました。
この処理が正しく動いてる環境においては、独自型で取りうる値を制御できるので、引数の値をバリデーションする処理は不要です。
ですが、 1.10
もしくは 1.11
を動かすと以下のような結果になります。
yes
jop
jopが返ってきてしまっています。これは正しくありませんね。
もう一つ似たような処理を書きます。
package main
import "fmt"
type MyString = string
const (
YES MyString = "yes"
NO = "no"
DONTKNOW = "i dont know"
)
func foo(arg MyString) {
fmt.Println(arg)
}
func main() {
foo(YES) //success, prints "yes"
foo("jop") //success, prints "jop"
}
型指定のところが type MyString string
だったところが、 type MyString = string
になっています。
とても似ていますが、これはGo 1.9で導入された。 Type Alias
と呼ばれる処理で、MyString
はstring
と同じ型として扱われます。
こちらに関しては、詳しく書かれている方がいるのでそちらを参照してください。
go言語1.9で追加予定の新機能 型エイリアス
https://qiita.com/weloan/items/8abbb4003cfa1031a9e9
こちらは、MyString
はstring
と同等ですので結果は
yes
jop
で、これが正しい結果です。
Goのリポジトリにもissueとしてあがっていますが、このType Alias
が問題を引き起こしているようです。
https://github.com/golang/go/issues/26390
問題はすでに修正済みで1.11
のマイルストーンとして解決しているようです。
ですが、このようにも記載されていました
the real fix is slated for 1.12
-- 実際の修正は1.12に予定されています
1.12.1 では、直ってなさそうです
結論
1.10
及び1.11
では独自型で値の制限はかけられません。
値を制限したい場合はバリデーション処理を独自に作ってください。