Конспект: Основы срезов (slices)

1. Определение и синтаксис

Срез — динамическая структура данных, обёртка над массивом.

Объявление

var slice []int  // БЕЗ размера в []

Отличие от массива:

var array [3]int  // Массив — с размером
var slice []int   // Срез — без размера

2. Ключевые свойства

Свойство Описание
Динамическая структура Можно добавлять/удалять элементы
Обёртка над массивом Данные хранятся в массиве под капотом
Ссылочный тип Копируется указатель, не данные
Может быть nil В отличие от массива

3. Nil-срез

var slice1 []int
fmt.Println(slice1)        // []
fmt.Println(slice1 == nil) // true

Важно: Неинициализированный срез равен nil.

Массив vs Срез

var array [3]int
// array == nil  // ❌ Ошибка компиляции — массив не может быть nil

var slice []int
fmt.Println(slice == nil)  // ✅ true

4. Инициализация среза

Способ 1: Литерал

slice2 := []int{1, 2, 3, 4, 5}
fmt.Println(slice2)        // [1 2 3 4 5]
fmt.Println(slice2 == nil) // false

Срез с данными не равен nil.


Способ 2: Пустой срез

slice3 := []int{}
fmt.Println(slice3)        // []
fmt.Println(slice3 == nil) // false
fmt.Println(len(slice3))   // 0

Критическое различие:

var s1 []int     // nil, len=0
s2 := []int{}    // не nil, len=0

Оба выглядят как [], но:

  • s1nil (указатель не инициализирован)
  • s2не nil (указатель на пустой массив)

Способ 3: Функция make()

slice4 := make([]int, 0)
fmt.Println(slice4 == nil) // false

Эквивалент:

slice3 := []int{}         // Короткая форма
slice4 := make([]int, 0)  // Явная форма

5. Сравнение: nil vs пустой срез

var nilSlice []int
emptySlice := []int{}

fmt.Println(nilSlice == nil)   // true
fmt.Println(emptySlice == nil) // false

fmt.Println(len(nilSlice))     // 0
fmt.Println(len(emptySlice))   // 0

Визуально одинаковы, семантически различны:

Срез Состояние nil? len
var s []int Не инициализирован ✅ Да 0
s := []int{} Инициализирован пустым ❌ Нет 0
s := make([]int, 0) Инициализирован пустым ❌ Нет 0

6. Копирование среза (ссылочный тип)

slice1 := []int{1, 2, 3}
slice2 := slice1  // Копируется только ссылка!

slice2[0] = 999

fmt.Println(slice1)  // [999 2 3] — изменился!
fmt.Println(slice2)  // [999 2 3]

Механизм:

slice1 ───┐
          ├──→ [999, 2, 3]  (один массив в памяти)
slice2 ───┘

Отличие от массива:

array1 := [3]int{1, 2, 3}
array2 := array1  // Полная копия всех элементов

array2[0] = 999

fmt.Println(array1)  // [1 2 3]   — не изменился
fmt.Println(array2)  // [999 2 3]

7. Полный пример

package main

import "fmt"

func main() {
    // 1. Nil-срез
    var s1 []int
    fmt.Printf("s1: %v, nil=%t, len=%d\n", s1, s1 == nil, len(s1))
    // s1: [], nil=true, len=0
    
    // 2. Литерал с данными
    s2 := []int{1, 2, 3, 4, 5}
    fmt.Printf("s2: %v, nil=%t, len=%d\n", s2, s2 == nil, len(s2))
    // s2: [1 2 3 4 5], nil=false, len=5
    
    // 3. Пустой срез
    s3 := []int{}
    fmt.Printf("s3: %v, nil=%t, len=%d\n", s3, s3 == nil, len(s3))
    // s3: [], nil=false, len=0
    
    // 4. Через make
    s4 := make([]int, 0)
    fmt.Printf("s4: %v, nil=%t, len=%d\n", s4, s4 == nil, len(s4))
    // s4: [], nil=false, len=0
}

8. Итоги

✅ Срез = динамическая обёртка над массивом ✅ Синтаксис: []T без указания размера
Ссылочный тип — изменения видны через все копии ✅ Может быть nil — если не инициализирован ✅ []int{}nil, но len = 0 в обоих случаях

Ключевое правило:

var s []int     → nil-срез
s := []int{}    → пустой срез (не nil)
s := []int{1,2} → срез с данными

Следующий шаг: Функция make(), len(), cap(), операции со срезами.