Skip to content
Theory · Maps · lesson 6 of 9

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:

GO
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:

  1. The variable m is nil, so the first line prints true.
  2. On the second line len returns 0.
  3. Reading the key "kiwi" returns the zero value 0.

None of the lines causes an error.

Reading a nil map

For reads, a nil map behaves like an empty map:

  • the len function returns 0;
  • any key reads as the zero value, and comma ok gives false;
  • a range loop over it never runs its body;
  • delete does nothing;
  • the call fmt.Println(m) prints map[], 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:

GO
var m map[string]int
fmt.Println(len(m))
m["a"] = 1 // ✗
fmt.Println(m)
TEXT
panic: assignment to entry in nil map

Let's see what happens here:

  1. fmt.Println(len(m)) prints 0.
  2. 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.
  3. The last fmt.Println never 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:

GO
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: len gives 0, any key gives the zero value, range runs 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) or map[string]int{}.

Subscribe to take the course

The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.

Sign in another way