[golang] nil マップと empty マップの特徴と使い分け

# 目的
nil マップと empty マップの特徴と使い分けを理解する。

# 検証
```
~/praprago$ go version 
go version go1.23.2 linux/amd64
```
``` main.go
package main

import "fmt"

// go run .
func main() {
	fmt.Println(" ======================================== (1)")
	pra_map_nil_1()
	fmt.Println(" ======================================== (2)")
	pra_map_nil_2()
}

```
``` pra_map_nil.go
package main

import (
	"encoding/json"
	"fmt"
	"unsafe"
)

// //////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////// (1)
// nil マップと empty マップの違いを示している。
// nil マップは、メモリが割り当てられていないので、特に理由がない限り、関数の返り値としては nil マップを返すのがよい。
// 「nil マップ or empty マップ」 の条件で判定したい場合は、len() で判定するのがよい。
func pra_map_nil_1() {
	var myMap map[string]string
	myMap2 := map[string]string(nil)
	myMap3 := map[string]string{}
	myMap4 := make(map[string]string)

	if myMap == nil {
		println("myMap is nil")
	} else {
		println("myMap is not nil")
	}
	if myMap2 == nil {
		println("myMap2 is nil")
	} else {
		println("myMap2 is not nil")
	}
	if myMap3 == nil {
		println("myMap3 is nil")
	} else {
		println("myMap3 is not nil")
	}
	if myMap4 == nil {
		println("myMap4 is nil")
	} else {
		println("myMap4 is not nil")
	}

	if len(myMap) == 0 {
		println("myMap is empty")
	} else {
		println("myMap is not empty")
	}
	if len(myMap2) == 0 {
		println("myMap2 is empty")
	} else {
		println("myMap2 is not empty")
	}
	if len(myMap3) == 0 {
		println("myMap3 is empty")
	} else {
		println("myMap3 is not empty")
	}
	if len(myMap4) == 0 {
		println("myMap4 is empty")
	} else {
		println("myMap4 is not empty")
	}

	println("")
	fmt.Println(" ===== myMap")
	printMapDataInfo("myMap", myMap)
	fmt.Println(" ===== myMap2")
	printMapDataInfo("myMap2", myMap2)
	fmt.Println(" ===== myMap3")
	printMapDataInfo("myMap3", myMap3)
	fmt.Println(" ===== myMap4")
	printMapDataInfo("myMap4", myMap4)
}

// スライスの unsafe.SliceData(slice) のようにデータ部分の先頭を示すポインタを取得する方法がない。
// そもそも、map のでーた部分の先頭ってどこだよって話だが。
// とりあえず、unsafe.Pointer() でポインタを取得してみたが、これは map そのもののポインタを取得しているだけで、データ部分の先頭を示すわけではないっぽい。
func printMapDataInfo(name string, m map[string]string) {
	println(name, "length:", len(m))
	dataPtr := unsafe.Pointer(&m)
	println(name, "Map pointer:", dataPtr)
}

// //////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////// (2)
// nil マップは null になるが、empty マップは {} になる。
type MyStruct struct {
	Name   string            `json:"name"`
	Values map[string]string `json:"values"`
}

func pra_map_nil_2() {
	struct1 := MyStruct{Name: "struct1", Values: nil}
	struct2 := MyStruct{Name: "struct2", Values: map[string]string(nil)}
	struct3 := MyStruct{Name: "struct3", Values: map[string]string{}}
	struct4 := MyStruct{Name: "struct4", Values: make(map[string]string)}

	jsonStruct1, _ := json.Marshal(struct1)
	jsonStruct2, _ := json.Marshal(struct2)
	jsonStruct3, _ := json.Marshal(struct3)
	jsonStruct4, _ := json.Marshal(struct4)

	println("struct1 JSON:", string(jsonStruct1))
	println("struct2 JSON:", string(jsonStruct2))
	println("struct3 JSON:", string(jsonStruct3))
	println("struct4 JSON:", string(jsonStruct4))
}

```
```
~/praprago$ go build -gcflags "-m=2" .
# github.com/XXX/praprago
./pra_map_nil.go:76:6: can inline printMapDataInfo with cost 15 as: func(string, map[string]string) { println(name, "length:", len(m)); dataPtr := unsafe.Pointer(&m); println(name, "Map pointer:", dataPtr) }
./pra_map_nil.go:14:6: cannot inline pra_map_nil_1: function too complex: cost 478 exceeds budget 80
./pra_map_nil.go:90:6: cannot inline pra_map_nil_2: function too complex: cost 317 exceeds budget 80
./main.go:6:6: cannot inline main: function too complex: cost 274 exceeds budget 80
./main.go:7:13: inlining call to fmt.Println
./main.go:9:13: inlining call to fmt.Println
./pra_map_nil.go:63:13: inlining call to fmt.Println
./pra_map_nil.go:64:18: inlining call to printMapDataInfo
./pra_map_nil.go:65:13: inlining call to fmt.Println
./pra_map_nil.go:66:18: inlining call to printMapDataInfo
./pra_map_nil.go:67:13: inlining call to fmt.Println
./pra_map_nil.go:68:18: inlining call to printMapDataInfo
./pra_map_nil.go:69:13: inlining call to fmt.Println
./pra_map_nil.go:70:18: inlining call to printMapDataInfo
./pra_map_nil.go:69:14: " ===== myMap4" escapes to heap:
./pra_map_nil.go:69:14:   flow: {storage for ... argument} = &{storage for " ===== myMap4"}:
./pra_map_nil.go:69:14:     from " ===== myMap4" (spill) at ./pra_map_nil.go:69:14
./pra_map_nil.go:69:14:     from ... argument (slice-literal-element) at ./pra_map_nil.go:69:13
./pra_map_nil.go:69:14:   flow: fmt.a = &{storage for ... argument}:
./pra_map_nil.go:69:14:     from ... argument (spill) at ./pra_map_nil.go:69:13
./pra_map_nil.go:69:14:     from fmt.a := ... argument (assign-pair) at ./pra_map_nil.go:69:13
./pra_map_nil.go:69:14:   flow: {heap} = *fmt.a:
./pra_map_nil.go:69:14:     from fmt.Fprintln(os.Stdout, fmt.a...) (call parameter) at ./pra_map_nil.go:69:13
./pra_map_nil.go:67:14: " ===== myMap3" escapes to heap:
./pra_map_nil.go:67:14:   flow: {storage for ... argument} = &{storage for " ===== myMap3"}:
./pra_map_nil.go:67:14:     from " ===== myMap3" (spill) at ./pra_map_nil.go:67:14
./pra_map_nil.go:67:14:     from ... argument (slice-literal-element) at ./pra_map_nil.go:67:13
./pra_map_nil.go:67:14:   flow: fmt.a = &{storage for ... argument}:
./pra_map_nil.go:67:14:     from ... argument (spill) at ./pra_map_nil.go:67:13
./pra_map_nil.go:67:14:     from fmt.a := ... argument (assign-pair) at ./pra_map_nil.go:67:13
./pra_map_nil.go:67:14:   flow: {heap} = *fmt.a:
./pra_map_nil.go:67:14:     from fmt.Fprintln(os.Stdout, fmt.a...) (call parameter) at ./pra_map_nil.go:67:13
./pra_map_nil.go:65:14: " ===== myMap2" escapes to heap:
./pra_map_nil.go:65:14:   flow: {storage for ... argument} = &{storage for " ===== myMap2"}:
./pra_map_nil.go:65:14:     from " ===== myMap2" (spill) at ./pra_map_nil.go:65:14
./pra_map_nil.go:65:14:     from ... argument (slice-literal-element) at ./pra_map_nil.go:65:13
./pra_map_nil.go:65:14:   flow: fmt.a = &{storage for ... argument}:
./pra_map_nil.go:65:14:     from ... argument (spill) at ./pra_map_nil.go:65:13
./pra_map_nil.go:65:14:     from fmt.a := ... argument (assign-pair) at ./pra_map_nil.go:65:13
./pra_map_nil.go:65:14:   flow: {heap} = *fmt.a:
./pra_map_nil.go:65:14:     from fmt.Fprintln(os.Stdout, fmt.a...) (call parameter) at ./pra_map_nil.go:65:13
./pra_map_nil.go:63:14: " ===== myMap" escapes to heap:
./pra_map_nil.go:63:14:   flow: {storage for ... argument} = &{storage for " ===== myMap"}:
./pra_map_nil.go:63:14:     from " ===== myMap" (spill) at ./pra_map_nil.go:63:14
./pra_map_nil.go:63:14:     from ... argument (slice-literal-element) at ./pra_map_nil.go:63:13
./pra_map_nil.go:63:14:   flow: fmt.a = &{storage for ... argument}:
./pra_map_nil.go:63:14:     from ... argument (spill) at ./pra_map_nil.go:63:13
./pra_map_nil.go:63:14:     from fmt.a := ... argument (assign-pair) at ./pra_map_nil.go:63:13
./pra_map_nil.go:63:14:   flow: {heap} = *fmt.a:
./pra_map_nil.go:63:14:     from fmt.Fprintln(os.Stdout, fmt.a...) (call parameter) at ./pra_map_nil.go:63:13
./pra_map_nil.go:17:29: map[string]string{} does not escape
./pra_map_nil.go:18:16: make(map[string]string) does not escape
./pra_map_nil.go:63:13: ... argument does not escape
./pra_map_nil.go:63:14: " ===== myMap" escapes to heap
./pra_map_nil.go:65:13: ... argument does not escape
./pra_map_nil.go:65:14: " ===== myMap2" escapes to heap
./pra_map_nil.go:67:13: ... argument does not escape
./pra_map_nil.go:67:14: " ===== myMap3" escapes to heap
./pra_map_nil.go:69:13: ... argument does not escape
./pra_map_nil.go:69:14: " ===== myMap4" escapes to heap
./pra_map_nil.go:96:33: struct1 escapes to heap:
./pra_map_nil.go:96:33:   flow: {heap} = &{storage for struct1}:
./pra_map_nil.go:96:33:     from struct1 (spill) at ./pra_map_nil.go:96:33
./pra_map_nil.go:96:33:     from json.Marshal(struct1) (call parameter) at ./pra_map_nil.go:96:32
./pra_map_nil.go:97:33: struct2 escapes to heap:
./pra_map_nil.go:97:33:   flow: {heap} = &{storage for struct2}:
./pra_map_nil.go:97:33:     from struct2 (spill) at ./pra_map_nil.go:97:33
./pra_map_nil.go:97:33:     from json.Marshal(struct2) (call parameter) at ./pra_map_nil.go:97:32
./pra_map_nil.go:98:33: struct3 escapes to heap:
./pra_map_nil.go:98:33:   flow: {heap} = &{storage for struct3}:
./pra_map_nil.go:98:33:     from struct3 (spill) at ./pra_map_nil.go:98:33
./pra_map_nil.go:98:33:     from json.Marshal(struct3) (call parameter) at ./pra_map_nil.go:98:32
./pra_map_nil.go:99:33: struct4 escapes to heap:
./pra_map_nil.go:99:33:   flow: {heap} = &{storage for struct4}:
./pra_map_nil.go:99:33:     from struct4 (spill) at ./pra_map_nil.go:99:33
./pra_map_nil.go:99:33:     from json.Marshal(struct4) (call parameter) at ./pra_map_nil.go:99:32
./pra_map_nil.go:94:51: make(map[string]string) escapes to heap:
./pra_map_nil.go:94:51:   flow: struct4 = &{storage for make(map[string]string)}:
./pra_map_nil.go:94:51:     from make(map[string]string) (spill) at ./pra_map_nil.go:94:51
./pra_map_nil.go:94:51:     from MyStruct{...} (struct literal element) at ./pra_map_nil.go:94:21
./pra_map_nil.go:94:51:     from struct4 := MyStruct{...} (assign) at ./pra_map_nil.go:94:10
./pra_map_nil.go:94:51:   flow: {storage for struct4} = struct4:
./pra_map_nil.go:94:51:     from struct4 (interface-converted) at ./pra_map_nil.go:99:33
./pra_map_nil.go:93:64: map[string]string{} escapes to heap:
./pra_map_nil.go:93:64:   flow: struct3 = &{storage for map[string]string{}}:
./pra_map_nil.go:93:64:     from map[string]string{} (spill) at ./pra_map_nil.go:93:64
./pra_map_nil.go:93:64:     from MyStruct{...} (struct literal element) at ./pra_map_nil.go:93:21
./pra_map_nil.go:93:64:     from struct3 := MyStruct{...} (assign) at ./pra_map_nil.go:93:10
./pra_map_nil.go:93:64:   flow: {storage for struct3} = struct3:
./pra_map_nil.go:93:64:     from struct3 (interface-converted) at ./pra_map_nil.go:98:33
./pra_map_nil.go:93:64: map[string]string{} escapes to heap
./pra_map_nil.go:94:51: make(map[string]string) escapes to heap
./pra_map_nil.go:96:33: struct1 escapes to heap
./pra_map_nil.go:97:33: struct2 escapes to heap
./pra_map_nil.go:98:33: struct3 escapes to heap
./pra_map_nil.go:99:33: struct4 escapes to heap
./pra_map_nil.go:101:34: string(jsonStruct1) does not escape
./pra_map_nil.go:102:34: string(jsonStruct2) does not escape
./pra_map_nil.go:103:34: string(jsonStruct3) does not escape
./pra_map_nil.go:104:34: string(jsonStruct4) does not escape
./main.go:9:14: " ======================================== (2)" escapes to heap:
./main.go:9:14:   flow: {storage for ... argument} = &{storage for " ======================================== (2)"}:
./main.go:9:14:     from " ======================================== (2)" (spill) at ./main.go:9:14
./main.go:9:14:     from ... argument (slice-literal-element) at ./main.go:9:13
./main.go:9:14:   flow: fmt.a = &{storage for ... argument}:
./main.go:9:14:     from ... argument (spill) at ./main.go:9:13
./main.go:9:14:     from fmt.a := ... argument (assign-pair) at ./main.go:9:13
./main.go:9:14:   flow: {heap} = *fmt.a:
./main.go:9:14:     from fmt.Fprintln(os.Stdout, fmt.a...) (call parameter) at ./main.go:9:13
./main.go:7:14: " ======================================== (1)" escapes to heap:
./main.go:7:14:   flow: {storage for ... argument} = &{storage for " ======================================== (1)"}:
./main.go:7:14:     from " ======================================== (1)" (spill) at ./main.go:7:14
./main.go:7:14:     from ... argument (slice-literal-element) at ./main.go:7:13
./main.go:7:14:   flow: fmt.a = &{storage for ... argument}:
./main.go:7:14:     from ... argument (spill) at ./main.go:7:13
./main.go:7:14:     from fmt.a := ... argument (assign-pair) at ./main.go:7:13
./main.go:7:14:   flow: {heap} = *fmt.a:
./main.go:7:14:     from fmt.Fprintln(os.Stdout, fmt.a...) (call parameter) at ./main.go:7:13
./main.go:7:13: ... argument does not escape
./main.go:7:14: " ======================================== (1)" escapes to heap
./main.go:9:13: ... argument does not escape
./main.go:9:14: " ======================================== (2)" escapes to heap
./pra_map_nil.go:76:23: name does not escape
./pra_map_nil.go:76:36: m does not escape
```


# 参考

https://www.amazon.co.jp/-/en/Teiva-Harsanyi/dp/4295017531

https://go.dev/blog/maps

https://pkg.go.dev/maps#pkg-index

https://stackoverflow.com/questions/12544641/how-to-count-items-in-a-go-map

https://go.dev/ref/spec#Length_and_capacity
> ```
> Call      Argument type    Result
>
> len(s)    string type      string length in bytes
>           [n]T, *[n]T      array length (== n)
>           []T              slice length
>           map[K]T          map length (number of defined keys)
>           chan T           number of elements queued in channel buffer
>           type parameter   see below
> 
> cap(s)    [n]T, *[n]T      array length (== n)
>           []T              slice capacity
>           chan T           channel buffer capacity
>           type parameter   see below
> ```

https://pkg.go.dev/unsafe#pkg-variables