Создание срезов через make() и slicing

1. Функция make() для создания срезов

Синтаксис с двумя аргументами

make([]тип, длина)

Создаёт срез с len и cap, равными указанному значению, заполненный нулями:

slice := make([]int, 5)
fmt.Println(slice)          // [0 0 0 0 0]
fmt.Println(len(slice))     // 5
fmt.Println(cap(slice))     // 5

Синтаксис с тремя аргументами

make([]тип, длина, ёмкость)

Создаёт срез с указанными len и cap:

slice := make([]int, 3, 10)
fmt.Println(slice)          // [0 0 0]
fmt.Println(len(slice))     // 3
fmt.Println(cap(slice))     // 10

Важно: cap >= len всегда.


2. Нарезка (slicing) массивов и срезов

Синтаксис

array[start:end]  // От start включительно до end невключительно

Варианты использования

Синтаксис Описание
arr[:3] Первые 3 элемента (индексы 0, 1, 2)
arr[2:] С индекса 2 до конца
arr[:] Все элементы
arr[1:4] Элементы с индексами 1, 2, 3

3. Примеры нарезки

arr := [7]int{10, 20, 30, 40, 50, 60, 70}
//     индексы: 0   1   2   3   4   5   6

arr[1:4] — элементы 1, 2, 3

slice := arr[1:4]
fmt.Println(slice)     // [20 30 40]
fmt.Println(len(slice)) // 3
fmt.Println(cap(slice)) // 6

Почему cap = 6? Ёмкость считается от начала среза до конца массива: 7 - 1 = 6.


arr[:3] — первые три

slice := arr[:3]
fmt.Println(slice)  // [10 20 30]

Эквивалентно: arr[0:3]


arr[4:] — с четвёртого до конца

slice := arr[4:]
fmt.Println(slice)  // [50 60 70]

Индексы: 4, 5, 6.


arr[:] — копия всех элементов

slice := arr[:]
fmt.Println(slice)  // [10 20 30 40 50 60 70]

Важно: Это не глубокая копия — срез указывает на тот же массив.


4. Длина и ёмкость при slicing

data := [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
s := data[2:5]

fmt.Println(s)         // [2 3 4]
fmt.Println(len(s))    // 3 — количество элементов (5-2)
fmt.Println(cap(s))    // 8 — от индекса 2 до конца массива (10-2)

Формула cap для среза:

cap = len(базовый_массив) - start_индекс

5. Функция append()

Важное правило

Результат append() ВСЕГДА нужно присваивать:

vals := []int{1, 2, 3}
vals = append(vals, 4)  // ✅ Правильно

Ошибка:

append(vals, 4)  // Результат потерян!

Добавление одного элемента

vals := []int{1, 2, 3}
fmt.Printf("До: %v (len=%d, cap=%d)\n", vals, len(vals), cap(vals))
// До: [1 2 3] (len=3, cap=3)

vals = append(vals, 4)
fmt.Printf("После: %v (len=%d, cap=%d)\n", vals, len(vals), cap(vals))
// После: [1 2 3 4] (len=4, cap=6)

Ёмкость увеличилась с 3 до 6 автоматически.


Добавление нескольких элементов

vals = append(vals, 5, 6, 7)
fmt.Println(vals)  // [1 2 3 4 5 6 7]

6. Механика роста ёмкости (повторение)

Правило

  • len < cap: добавление без перевыделения
  • len == cap: создаётся новый массив с увеличенной ёмкостью

Стратегия роста

Малые срезы (cap < ~256):  cap × 2
Большие срезы:             cap × 1.25 (примерно)

Пример роста

growth := make([]int, 0, 2)
fmt.Printf("Начало: len=%d, cap=%d\n", len(growth), cap(growth))
// Начало: len=0, cap=2

for i := 0; i < 10; i++ {
    growth = append(growth, i)
    fmt.Printf("i=%d: len=%d, cap=%d\n", i, len(growth), cap(growth))
}

Вывод:

i=0: len=1, cap=2
i=1: len=2, cap=2
i=2: len=3, cap=4   ← удвоение
i=3: len=4, cap=4
i=4: len=5, cap=8   ← удвоение
i=5: len=6, cap=8
i=6: len=7, cap=8
i=7: len=8, cap=8
i=8: len=9, cap=16  ← удвоение
i=9: len=10, cap=16

7. Важность больших тестов

Совет из материала:
Тестируйте на больших объёмах (500k элементов), а не на малых (2-10 элементов).

Почему:

  • На малых данных кажется, что рост всегда ×2
  • На больших — переход к росту ~25%
  • Точная формула может меняться между версиями Go

8. Ключевые правила len и cap

Утверждение Пояснение
cap >= len Всегда истинно
cap после make([]T, n) Равен n
cap после make([]T, n, m) Равен m
cap после arr[i:j] len(arr) - i
cap растёт при append Только если len == cap

9. Итоги

make([]T, len) — срез длины len, заполнен нулями
make([]T, len, cap) — срез с резервом ёмкости
arr[start:end] — нарезка от start до end-1
arr[:] — срез всех элементов массива
append() всегда требует присваивания результата
cap среза = от начала среза до конца базового массива
✅ Тестируйте рост на больших данных для понимания поведения

Формула slicing:

arr[start:end]
len = end - start
cap = len(arr) - start