Что выведет сравнение nil-среза и пустого среза?
Определите, что это выведет, затем назовите практическую разницу между nil-срезом и пустым ненулевым срезом — включая len, cap, поведение append/range и то, как каждый маршалится в JSON.
var a []int // nil slice
b := []int{} // empty, non-nil slice
fmt.Println(a == nil, b == nil) // ?
fmt.Println(len(a), cap(a), len(b)) // ?
Определите вывод.
nil-срез (var a []int) не имеет backing array, и a == nil равно true; пустой срез ([]int{}) ненулевой, с len 0. Оба поддерживают append и range. Для «нет результатов» предпочитают nil — но nil-срез маршалится в JSON как null, а пустой — как [].
- ✗Считать, что в
nil-срез нельзя делатьappend—appendвыделяет backing array при первом росте - ✗Полагать, что
[]int{}равенnil— он ненулевой - ✗Упускать, что
nil-срез маршалится в JSON какnull, а не[]
- →Почему
appendработает одинаково наnil-срезе и пустом, несмотря на разницу== nil? - →Когда различие JSON
nullпротив[]реально ломает контракт API?
Что выведет код?
var a []int // nil-срез
b := []int{} // пустой ненулевой срез
fmt.Println(a == nil, b == nil) // ?
fmt.Println(len(a), cap(a), len(b)) // ?
Вывод
true false
0 0 0
Разница
nil-срез (var a []int) | пустой срез ([]int{}) | |
|---|---|---|
== nil | true | false |
| backing array | отсутствует | выделен (длина 0) |
len / cap | 0 / 0 | 0 / 0 |
append | работает | работает |
range | 0 итераций | 0 итераций |
| JSON-маршалинг | null | [] |
С точки зрения чтения, append и range оба среза неотличимы — поэтому идиоматично возвращать nil для «результатов нет» вместо лишней аллокации []int{}.
⚠️ Единственное место, где разница важна, — сериализация: json.Marshal(nil-срез) даёт null, а пустой срез — []. Если клиент API ждёт массив, верните []int{} или нормализуйте на границе.