W Go zmienne zadeklarowane bez jawnego inicjalizatora automatycznie otrzymują tak zwane wartości zerowe (zero values), które są różne dla różnych typów:
int — 0bool — falsestring — ""Jest to wygodne (brak śmieci w pamięci), ale może być niebezpieczne: dla niektórych typów, na przykład slice'ów i map, operacje na wartości nil mogą prowadzić do paniki lub błędów.
Przykład:
var m map[string]int m["key"] = 1 // panic: assignment to entry in nil map var s []int fmt.Println(s, s == nil) // [] true (można dodawać elementy, ale nie można odwoływać się przez indeks)
Czym różni się wartość nil slice'a od wartości nil mapy/kanału i do jakich błędów to prowadzi?
Poprawna odpowiedź: Slice z wartością nil można przekazywać do funkcji, używać w append i range — to jest bezpieczne, ale praca z mapą nil spowoduje panikę przy próbie zapisu (ale odczyt jest dozwolony i zwróci wartość zerową).
Przykład:
var m map[string]int fmt.Println(m["no_key"]) // 0 — bezpiecznie m["key"] = 1 // panic! var s []int s = append(s, 42) // ok
Historia
Opis: W dużym projekcie używano nieinicjalizowanych map do agregacji danych. Przy pierwszym zapisie aplikacja stabilnie padała z panic. Problem wykryto w produkcji, gdy wprowadzono nowe dane statystyczne.
Historia
Opis: Serwis serializował nieinicjalizowany slice, wysyłając go jako JSON w odpowiedzi API. Zamiast
[]klienci otrzymywalinull— trzeba było zmieniać schemat na froncie, aby obsługiwać oba warianty.
Historia
Opis: W module cache'ującym błędnie porównywano nil map z pustą mapą i przez to błędnie określano: czy są elementy. Prowadziło to do zbędnych odwołań do bazy danych, obniżając wydajność.