技術メモ

技術メモ

ラフなメモ

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 にもある。以下のような実装である。

BadGood
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 を用いて初期化する、で良さそうである。

参考