Срезы и aliasing
В предпросмотре импорта Миша взял кусок списка и дописал туда пробную запись. Наутро клиентский список показал чужое имя на месте своего — пробная запись заехала на хвост. Разбирались с Аней до вечера.
срез s[low:high] создаёт новый заголовок на тот же массив, поэтому append к срезу может записать в хвост исходного слайса; [low:high:max] ограничивает ёмкость.
Срез делит массив
Что здесь показано
- Стек — локальные переменные функции.
i = 3— значение;p = ptr → 10— указатель на значение;b = "hello"— срез (заголовок: указатель + длина + ёмкость). - Куча — данные, на которые ссылаются переменные:
rodata— байты строки-литерала (в бинарнике),данные N Б— бэкинг-массив слайса,строка— динамическая строка. - Горутины — лёгкие потоки Go: статус (running/runnable/waiting…), функция, в которой каждая сейчас находится, размер стека (растёт по мере работы) и пометка «из main» — горутина запущена из твоего кода.
- GC — сборщик мусора: фаза (off/mark) и число завершённых циклов.
Индексы полуоткрытые: low включается, high — нет. Ёмкость среза считается от low до конца подлежащего массива, поэтому у window длина 3, а ёмкость 4 — за элементом 40 в исходном массиве лежит ещё 50.
Append задевает исходник
window = append(window, 60)
fmt.Println(ids) // [10 20 30 40 60] — append перезаписал хвост исходного массива
append дописал 60 в ячейку, где раньше лежал 50. Для ids это выглядит как порча данных. Такое aliasing — самый частый источник багов со слайсами: два слайса делят массив, и один «незаметно» меняет данные другого.
Ограничить ёмкость
Третий индекс, full slice expression, фиксирует cap как max-low:
ids = []int{10, 20, 30, 40, 50} // сбрасываем ids после примера 2: примеры независимы
window := ids[1:4:4]
fmt.Println(cap(window)) // 3 — ёмкость равна длине
window = append(window, 60) // места нет, append выделит новый массив
fmt.Println(ids) // [10 20 30 40 50] — исходник не тронут
Если нужно отрезать кусок так, чтобы дальнейший append гарантированно не трогал исходник, указывай max равным high. Это не полная изоляция: существующие элементы срез и исходник по-прежнему делят — запись window[0] = 99 изменит ids. Третий индекс ограничивает только будущий append.
Вы узнаете:
- почему отрицательные индексы и
low > highзапрещены - как считаются len/cap у срезов
s[:n],s[n:],s[:]
Проверьте себя
Предпросмотр импорта берёт срез списка файлов: a := []int{10, 20, 30, 40, 50}; s := a[2:4]. Чему равны len(s) и cap(s)?
s := a[:n:n] ограничивает ёмкость. Верно ли, что после этого append к результату гарантированно не тронет исходник?
Полный срез a[1:4:4] защищает существующие элементы от изменений?
правильных ответов
Срез — новое окно на старый массив. append пишет в доступный хвост, поэтому чужие данные можно «портить»; [low:high:max] отсекает хвост, но не отменяет общность существующих элементов.
10 000 ₽ · 60 минут · онлайн