Golangのnil sliceとnil map
nil sliceとempty sliceの違い
- nil sliceと空のsliceとは同じではないが、外から観測できる振る舞い(機能)は同じ
- len()もcap()もともに0を返す
package main import "fmt" func main() { var s1 []int s2 := []int{} s3 := make([]int, 0) s34= nil fmt.Println("s1", len(s1), cap(s1), s1 == nil, s1[:], s1[:] == nil) fmt.Println("s2", len(s2), cap(s2), s2 == nil, s2[:], s2[:] == nil) fmt.Println("s3", len(s3), cap(s3), s3 == nil, s3[:], s3[:] == nil) for range s1 { } for range s2 { } for range s3 { } }
s1 0 0 true [] true s2 0 0 false [] false s3 0 0 false [] false
https://play.golang.org/p/WS3K1gbbvxD
ポイントはsliceがnilかそうでないかは、nil sliceを宣言するか、そうでないかによって決まるということである。
ただしJSONをエンコードする場合など、一部の限定的な状況では空のsliceが好まれることがある。nil sliceは null
に変更されるが、空sliceは []
に変換される。
Tips
sliceが空かどうか判定するとき
空の配列かどうかチェックするにはnilを用いるのではなく、要素数が0がどうかで判断すべきということ。Uber Go Style Guide にもある。以下のような実装である。
Bad | Good |
---|---|
func isEmpty(s []string) bool { return s == nil } |
func isEmpty(s []string) bool { return len(s) == 0 } |
JSONを扱うとき
JSONを扱うときはnil sliceと空 sliceの違いに注意が必要である。nil sliceはJSONにエンコードすると null
に変換されるが、空のsliceは []
に変換される。
package main import ( "encoding/json" "fmt" ) func main() { var s1 []int jsonBytes, err := json.Marshal(s1) if err != nil { fmt.Println("JSON Marshal error:", err) return } fmt.Println(string(jsonBytes)) s2 := []int{} jsonBytes, err = json.Marshal(s2) if err != nil { fmt.Println("JSON Marshal error:", err) return } fmt.Println(string(jsonBytes)) }
https://play.golang.org/p/VdMwIqpZIQy
nil sliceへの要素のappend
機能的には要素数0の空のsliceと同じであってnil sliceに対してappendで要素を追加することもできる。
package main import ( "fmt" ) func main() { var s1 []string s1 = append(s1, "hoge") fmt.Println("s1", len(s1), cap(s1), s1 == nil, s1[:], s1[:] == nil) for _, s := range s1 { fmt.Println(s) } }
s1 1 1 false [hoge] false hoge
https://play.golang.org/p/8u5q00NwyAo
nil mapは要素の追加できない
なおnil mapは要素の追加ができない。以下のような実装はpanicになる。
package main func main() { var m map[string]float64 m["pi"] = 3.14 }
mapの処理化は Go maps in action を見る限り make
を用いて初期化する、で良さそうである。