Глиф Учебники

panic: assignment to entry in nil map

Справочник · Go · 1 минута чтения

Текст
panic: assignment to entry in nil map

Программа записала значение в map, которую не создали, и аварийно остановилась — в Go это называется паникой. Переменная, объявленная как var m map[string]int, равна nil: читать из неё можно (всегда получите нулевое значение), а записывать — нет. Решение — создать map до первой записи:

Go
m := make(map[string]int)
m["a"] = 1

или литералом:

Go
m := map[string]int{}

Где nil-map прячется

Поле структуры. Создание структуры не создаёт map в её полях — поле остаётся nil:

Go
type Inventory struct {
    Items map[string]int
}

var inv Inventory
inv.Items["яблоки"] = 5 // паника: Items == nil

Обычное решение — функция-конструктор, которая создаёт всё нужное:

Go
package main

import "fmt"

type Inventory struct {
    Items map[string]int
}

func NewInventory() *Inventory {
    return &Inventory{Items: make(map[string]int)}
}

func main() {
    inv := NewInventory()
    inv.Items["яблоки"] += 5
    inv.Items["яблоки"] += 2
    fmt.Println(inv.Items["яблоки"], len(inv.Items))
}
Вывод
7 1

Другой вариант — создавать map лениво, то есть только когда она понадобится: в методе, перед первой записью: if inv.Items == nil { inv.Items = make(…) }.

Map внутри map. Внешняя map создана, а внутренние — нет: для нового ключа outer["Минск"] возвращает nil.

Go
package main

import "fmt"

func main() {
    visits := make(map[string]map[string]int)

    add := func(city, page string) {
        if visits[city] == nil {
            visits[city] = make(map[string]int)
        }
        visits[city][page]++
    }

    add("Минск", "/")
    add("Минск", "/")
    add("Брест", "/news")
    fmt.Println(visits["Минск"]["/"], visits["Брест"]["/news"], len(visits))
}
Вывод
2 1 2

Без проверки if visits[city] == nil первая же запись visits[city][page]++ вызвала бы панику.

Map из функции или после json.Unmarshal. Функция может вернуть nil вместо map, а json.Unmarshal оставляет map в поле структуры равной nil, если такого поля в JSON нет. Проверьте результат перед записью или создайте map сами.

Что можно делать с nil-map

Нельзя только записывать. Поэтому ошибка часто всплывает не сразу, а там, где программа впервые что-то добавляет.

Как прочитать сообщение паники

Под первой строкой Go печатает стек — где именно случилась паника:

Текст
goroutine 1 [running]:
main.main()
    /home/user/hello/main.go:7 +0x…
exit status 2

Ищите первую строку с файлом своей программы — здесь это main.go:7, седьмая строка. Путь, номер строки и адреса у вас будут свои. Строка exit status 2 — это go run сообщает, что программа завершилась с ошибкой.