Введение в срезы
1. Чем срез отличается от массива
- Массив: фиксированная длина, размер входит в тип.
var arr [3]int // всегда длина 3, [0 0 0] // массив нельзя сравнить с nil - Срез: динамическая структура поверх массива.
var s []int // nil‑срез, длина 0, cap 0 fmt.Println(s) // [] fmt.Println(s == nil) // true
Ключевое: у массива всегда есть память под все элементы; у среза по умолчанию — только сама «обёртка», без выделенного массива под данные.
2. Внутреннее устройство среза
Срез состоит из трёх частей:
- указатель на базовый массив (pointer)
- длина (len) — сколько элементов «видит» срез
- вместимость (cap) — сколько максимум элементов можно положить, не перевыделяя память
Логически:
slice → (ptr, len, cap) → [элементы массива]
3. Создание и инициализация
Основные варианты:
// nil-срез
var s1 []int // nil, len=0, cap=0
// литерал среза
s2 := []int{1, 2, 3} // len=3, cap=3
// через make
s3 := make([]int, 0) // len=0, cap=0, но не nil
s4 := make([]int, 5) // len=5, cap=5, значения = 0
Массив vs срез по виду:
var a [3]int // массив: [3]int
var s []int // срез: []int
4. Копирование и «ссылочность»
- Массив при присваивании копируется целиком (deep copy).
- Срез — только структура, включая указатель (shallow copy).
s1 := []int{1, 2, 3}
s2 := s1 // оба смотрят на один базовый массив
s2[1] = 100
fmt.Println(s1) // [1 100 3]
fmt.Println(s2) // [1 100 3]
Изменения через один срез видны через другой, пока они ссылаются на один и тот же базовый массив.
5. Добавление элементов: append
s := []int{1, 2, 3}
s = append(s, 999) // всегда присваиваем результат
fmt.Println(s) // [1 2 3 999]
- При нехватке
capпод капотом создаётся новый массив, данные копируются, указатель в срезе меняется. - Поэтому после
appendнужно делать присваивание обратно в переменную.
6. Нарезка (slicing)
Срез по диапазону индексов исходного среза/массива:
s := []int{1, 2, 3, 4, 5}
a := s[:2] // [1 2]
b := s[2:] // [3 4 5]
c := s[1:4] // [2 3 4]
d := s[:] // полное «окно» на тот же базовый массив
Правило индексов: [start:end] — start включительно, end не включительно.
7. Удаление элемента через append
Специального метода remove нет, используем комбинацию slicing + append:
s := []int{1, 2, 3, 4, 5}
idx := 2 // хотим удалить элемент с индексом 2 (значение 3)
s = append(s[:idx], s[idx+1:]...)
fmt.Println(s) // [1 2 4 5]
Идея: берём всё до удаляемого и всё после него, склеиваем в один срез.
8. Главное, что нужно запомнить
- Срезы — динамическая обёртка над массивом:
(ptr, len, cap). - Это ссылочный тип: при присваивании копируется указатель, а не данные.
- Создаём срезы через
[]T{},make([]T, n)или нарезкой. - Элементы добавляем через
append, результат всегда присваиваем. - Удаление — через
append(s[:i], s[i+1:]...).
Если нужны динамические коллекции — выбираем срезы, а не массивы.