0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Posted at

目的

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

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?