目的
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
参考
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