Для чего используется синтаксис срезов в Go
Что проверяют
Понимаете ли вы, что срез в Go это не массив, а маленький заголовок поверх общего массива, и что выражение s[i:j] ничего не копирует. Следом обычно дают задачу с append в подсрез и спрашивают, что станет с исходными данными.
Как отвечать
Срез это структура из трёх полей: указатель на элемент базового массива, длина len и ёмкость cap. Синтаксис срезов нужен, чтобы получить новое окно на те же данные без копирования:
- простое выражение
a[low:high]: элементы сlowпоhigh-1, длинаhigh-low, ёмкостьcap(a)-low. Границы можно опускать:a[:],a[2:],a[:n]. Для срезаhighможет доходить доcap, а не только доlen, поэтомуs[:cap(s)]возвращает «спрятанные» элементы; - полное выражение
a[low:high:max]: дополнительно ограничивает ёмкость значениемmax-low. Тогда первый жеappendв подсрез выделит новый массив и не затрёт данные соседа; - срез от массива:
arr[:]превращает массив в срез без копирования, удобно передавать в функции, принимающие[]T; - строки:
str[i:j]работает по байтам, а не по символам, и тоже разделяет память с исходной строкой. Кириллический символ легко разрезать пополам, поэтому режьте по[]runeили по индексам изrange.
Главное следствие: подсрез и исходный срез видят одни и те же элементы, запись через один видна через другой, пока append не превысит ёмкость и не переедет в новый массив. Если нужна независимая копия, используйте copy или slices.Clone (пакет slices появился в стандартной библиотеке в Go 1.21).
Где спотыкаются
Считают, что b := a[1:3] создаёт копию, и меняют b, не ожидая изменений в a.
Делают append в подсрез с запасом ёмкости и тихо перетирают элементы исходного среза. Лечится полным выражением a[low:high:high] или явной копией.
Держат маленький подсрез от огромного буфера, например несколько байт от прочитанного файла. Весь массив остаётся в памяти, пока жив подсрез, поэтому нужный кусок стоит скопировать.