Goのnilのmapやスライスで許されない操作
Goのmapやスライスを操作するコードを書いていて、やたらとレビュー指摘をもらったのでここにダメな操作と大丈夫な操作をメモしておく。
例えば、こういうコードは余計なことをしているので直しなさいという指摘をもらった。
package main import "fmt" func main() { m := map[string][]int{} xs, ok := m["foo"] if !ok { xs = []int{} // nilのスライスが取れるから初期化した方がいいんじゃないか・・? } xs = append(xs, 10) m["foo"] = xs fmt.Println(m) }
これは、こう書けるらしい。
package main import "fmt" func main() { m := map[string][]int{} m["foo"] = append(m["foo"], 10) fmt.Println(m) }
結論から言うと、nilのmapにキー渡して値を入れようとするとpanicになるのでダメ。これ以外は大丈夫。
操作 | ok/NG | 結果 |
---|---|---|
nilのmapをlenに渡す 例: len(m) |
ok | 0を返す |
nilのmapをrangeで回す 例: for k, v := range m { ... } |
ok | 一度もループが回らない |
nilのmapからdeleteで要素を消す 例: delete(m, "foo") |
ok | 特に何も起きない |
nilのmapにキー渡して値を入れる 例: m["foo"] = 1 |
NG | panicになる (panic: assignment to entry in nil map) |
nilのスライスをlenやcapに渡す 例: len(xs), cap(xs) |
ok | 0を返す |
nilのスライスをrangeで回す 例: or _, x := range xs { ... } |
ok | 一度もループが回らない |
nilのスライスをappendで書き換える 例: xs = append(xs, 1) |
ok | 要素を追加できる |
Playgroundで試したサンプルコード
https://play.golang.org/p/Kje5KtMOMOw
package main import ( "fmt" ) func main() { var ( m map[string][]int = nil xs []int = nil ) fmt.Println("***** len, cap *****", len(xs), cap(xs)) // これは大丈夫: ***** len, cap ***** 0 0 for _, x := range xs { fmt.Println("*****", x) // 1回も通らない: でもrangeに渡されてもエラーにはならない } fmt.Println("===== len =====", len(m)) // これも大丈夫: ===== len ===== 0 for k, v := range m { fmt.Println("=====", k, v) // こっちも1回も通らない: エラーにはならない } delete(m, "par?") // nilのmapをdeleteに渡してもpanicにならない /* m["foo"] = []int{1, 2, 3} // これはダメ: panic: assignment to entry in nil map fmt.Println("m->", m) */ xs = append(xs, 10) // これは通る fmt.Println("xs->", xs) // xs-> [10] }