You define a Cache struct with a map field for its entries. You construct one with Cache{} — no map literal needed, Go fills in zero values for you, that's the whole appeal of structs. It compiles. It even reads back zero values fine if you check it before writing. Then the first write to it crashes the program outright, in code that looked like it was doing nothing unusual.
type Cache struct {
data map[string]int
}
c := Cache{} // data's zero value is nil, same as any uninitialized map
c.data["hits"] = 1 // panic: assignment to entry in nil mapThe next section explains exactly why, and the fix for the Cache case specifically sits right after it.
A map is Go’s built-in hash table type, written map[KeyType]ValueType. Keys must be a comparable type (numbers, strings, structs of comparable fields, and so on) — slices, maps, and functions cannot be keys because Go cannot compare them for equality. Iterating a map with range visits entries in an order that is deliberately randomized each run, specifically to stop code from ever relying on an order the language never guaranteed.
Creating and using maps
Maps are created with a literal or make, and support indexing, assignment, and delete:
ages := map[string]int{"Alice": 30, "Bob": 25} // map literal
ages["Carol"] = 28 // add/update a key
delete(ages, "Bob") // remove a key
fmt.Println(ages["Alice"]) // 30
fmt.Println(ages["Dave"]) // 0 — zero value, NOT an errorThe comma-ok idiom: telling "missing" from "zero"
Indexing a map for a missing key returns the value type's zero value, not an error — which is ambiguous if 0 or "" is also a valid stored value. The two-value form of a map read solves this: the second value is a bool reporting whether the key actually existed.
value, ok := ages["Dave"]
if !ok {
fmt.Println("Dave is not in the map") // this runs: ok is false
} else {
fmt.Println("Dave is", value)
}nil maps: readable, but panic on write
A map variable's zero value is nil, not an empty map. Reading from a nil map is safe and behaves like an empty map (returns the zero value), but writing to a nil map causes a runtime panic. A map must be initialized with make or a literal before you write to it.
var m map[string]int // nil map, not initialized
fmt.Println(m["x"]) // 0 — reading a nil map is safe
m["x"] = 1 // PANIC: assignment to entry in nil map
m = make(map[string]int) // now it's safe to write
m["x"] = 1 // fineWhich is exactly the Cache bug at the top of this page: a struct literal like Cache{} fills every field with its zero value, and a map field's zero value is nil — there's no map literal syntax forcing you to notice it the way there is for a standalone var m map[string]int. The fix is to initialize the map explicitly wherever the struct gets constructed:
c := Cache{data: make(map[string]int)} // data is a real, empty map now
c.data["hits"] = 1 // fine
fmt.Println(c.data) // map[hits:1]