The nil map
Imagine you have a label that says "Phone book", but no book yet. You can still answer the question "Is Ann in it?": no, nobody is in it at all. But you cannot write a new number into a book that does not exist.
In Go, such a label without a book appears after the declaration var m map[string]int. It sets up a variable but does not create the map itself. The zero value of a map type is nil, and such a variable is called a nil map. Reading from it works without errors:
package main
import "fmt"
func main() {
var m map[string]int
fmt.Println(m == nil) // true
fmt.Println(len(m), m["kiwi"]) // 0 0
}Let's walk through the output:
- The variable
misnil, so the first line printstrue. - On the second line
lenreturns0. - Reading the key
"kiwi"returns the zero value0.
None of the lines causes an error.
Reading a nil map
For reads, a nil map behaves like an empty map:
- the
lenfunction returns0; - any key reads as the zero value, and comma ok gives
false; - a
rangeloop over it never runs its body; deletedoes nothing;- the call
fmt.Println(m)printsmap[], as for an empty map.
Here the label picture is not exact. In real life, the honest answer about a book that does not exist is "there is no book". Go answers differently: as if the book exists and is empty. In a program, the comparison m == nil tells a nil map from an empty one. It is the only comparison allowed for a map: two maps, like two slices, cannot be compared with ==, and such code does not compile.
So a function that has nothing to return can return a nil map. The calling code reads zeros from the result and loops over it zero times, with no separate check for nil.
Writing to a nil map
Writing to a nil map causes a panic, an error at run time that stops the program:
var m map[string]int
fmt.Println(len(m))
m["a"] = 1 // ✗
fmt.Println(m)panic: assignment to entry in nil mapLet's see what happens here:
fmt.Println(len(m))prints0.- The program stops at the line
m["a"] = 1: a nil map has nowhere to write to, since no room for pairs has been set aside yet. - The last
fmt.Printlnnever runs.
The compiler does not catch this error: the build succeeds, and the panic happens at run time.
Interviewers like to ask: "What happens when you write to a nil map?" The answer is the panic assignment to entry in nil map. A common follow-up is that a nil slice behaves differently:
- you can add elements to a nil slice with
append; - you cannot write to a nil map.
Two ways to declare a map
You can write only to a map that has been created. So the way you declare a map depends on whether it will be written to.
A map created right away
The declaration m := make(map[string]int) or m := map[string]int{} creates an empty map, and such a map is no longer nil. You can write to it from the first line. Hence the rule: if a map will be written to, create it right away with make or a literal.
Declaring with var
The declaration var m map[string]int gives a nil map. It fits when the variable is later assigned a map that already exists, such as a function's result. The trap is that a write before that assignment causes a panic.
Common mistake
Inside a function, the result map is declared with var and never created:
func countLetters(s string) map[rune]int {
var counts map[rune]int // ✗
for _, r := range s {
counts[r]++
}
return counts
}The function works for an empty string. For any other string, it panics with assignment to entry in nil map on the first letter. Declaring with var is not the mistake in itself: the mistake is that the map stays nil. Declare the variable like this instead: counts := make(map[rune]int).
Key points
- The zero value of a map is
nil: the variable exists, but the map itself does not yet. - You can read from a nil map:
lengives0, any key gives the zero value,rangeruns zero times. - Writing to a nil map causes the panic
assignment to entry in nil map. - A map you will write to is created right away:
make(map[string]int)ormap[string]int{}.
Subscribe to take the course
The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.