Какое у slice zero value? Какие операции над ним возможны?go-79

Zero value для slice

В Go zero value для среза — это nil. Это означает, что объявление среза без инициализации создает nil-срез:

var s []int  // s == nil

Характеристики nil-среза:

  1. Длина (len) равна 0
  2. Емкость (cap) равна 0
  3. Не имеет выделенного базового массива
  4. Сравнение с nil возвращает true
fmt.Println(len(s))  // 0
fmt.Println(cap(s))  // 0
fmt.Println(s == nil) // true

Допустимые операции с nil-срезом

1. Чтение и получение свойств

Все операции, которые не требуют модификации памяти, работают с nil-срезом:

var s []int

// Получение длины и емкости
fmt.Println(len(s)) // 0
fmt.Println(cap(s)) // 0

// Итерация по срезу (не выполнит итераций)
for i, v := range s {
    fmt.Println(i, v) // Не выполнится
}

// Чтение элемента (паника!)
// v := s[0] // panic: runtime error: index out of range [0] with length 0

2. Добавление элементов

Единственная операция модификации, которая работает с nil-срезом:

var s []int
s = append(s, 1, 2, 3) // Создается новый массив
fmt.Println(s) // [1 2 3]

3. Срезы

Операции взятия среза работают с nil-срезом:

var s []int
subSlice := s[:] // Создает новый nil-срез
fmt.Println(subSlice == nil) // true

4. Копирование

Функция copy работает, но ничего не копирует:

var s []int
dst := make([]int, 3)
n := copy(dst, s) // n == 0, dst останется [0, 0, 0]

Недопустимые операции

  1. Индексация:
var s []int
v := s[0] // panic: index out of range
  1. Изменение элементов:
var s []int
s[0] = 1 // panic: index out of range
  1. Сравнение с не-nil срезами (кроме прямого сравнения с nil):
var s1 []int
s2 := []int{}
fmt.Println(s1 == s2) // Ошибка компиляции: slice can only be compared to nil

Практическое использование nil-срезов

  1. Как возвращаемое значение функций:
func findEvenNumbers(nums []int) []int {
    var result []int
    for _, n := range nums {
        if n%2 == 0 {
            result = append(result, n)
        }
    }
    return result // Вернет nil если четных чисел нет
}
  1. Как сигнал отсутствия данных:
func GetUserRoles(userID int) []string {
    if userID == 0 {
        return nil // Явный сигнал отсутствия данных
    }
    return []string{"user"}
}
  1. Оптимизация памяти:
// Лучше чем empty slice ([]int{}) когда данных нет
var activeConnections []*Connection // nil пока нет соединений

Отличие nil-среза от пустого среза

var nilSlice []int          // len=0, cap=0, nil
emptySlice := []int{}       // len=0, cap=0, not nil
zeroSlice := make([]int, 0) // len=0, cap=0, not nil

Ключевые различия:

  1. nilSlice == nil, а emptySlice != nil
  2. При сериализации (например, в JSON) nilSlice → null, emptySlice → []
  3. Семантически nil часто означает "отсутствие значения", а empty — "пустую коллекцию"

Резюмируем

zero value для среза в Go — это nil. С nil-срезом безопасно можно выполнять операции чтения метаданных (len, cap), итерации, append и slicing. Индексация и модификация элементов вызовут панику. Nil-срезы полезны как сигнал отсутствия данных и для оптимизации памяти, но важно понимать их отличие от пустых срезов.