Что выведет append(a[:1], 99) для a и b?
Определите два выведенных среза и объясните, делят ли a и b один backing array.
a := []int{1, 2, 3}
b := append(a[:1], 99)
fmt.Println(a, b)
Определите вывод.
Выведет [1 99 3] [1 99]. У a[:1] len 1, но cap 3, поэтому у append есть запас, и он пишет 99 в тот же backing array по индексу 1, перезаписывая a[1]. Принудить копию можно полным выражением среза a[:1:1], чтобы append реаллоцировал.
- ✗Считать, что
appendвсегда возвращает новый массив — он переиспользует backing array, когдаcap > len - ✗Путать
lenиcap— уa[:1]len 1, ноcap 3унаследован отa - ✗Забывать, что полное выражение среза
a[:1:1]ограничивает ёмкость, заставляя реаллокацию
- →Как
a[:1:1]меняет ёмкость и почему это предотвращает алиасинг? - →Если два вызова
appendделят один базовый срез с запасомcap, почему второй перезаписывает первый?
Что выведет код?
a := []int{1, 2, 3}
b := append(a[:1], 99)
fmt.Println(a, b)
Вывод
[1 99 3] [1 99]
Почему срезы делят память
a[:1] берёт первый элемент a, но наследует ёмкость исходного среза: len 1, cap 3.
У append есть запас (len 1 < cap 3), поэтому он не реаллоцирует — он пишет 99 в тот же backing array по индексу 1. Это перезаписывает a[1] (было 2), и a становится [1 99 3]. Возвращённый b имеет len 2: [1 99].
⚠️ Это типичная ловушка: «безобидный» append молча портит соседний срез, который делит память. Так же ломается код, где из одного базового среза с запасом cap делают два append — второй перезаписывает первый.
Исправление — полное выражение среза a[:1:1] (low:high:max), которое ставит cap == len, поэтому следующий append вынужден выделить новый массив:
b := append(a[:1:1], 99) // cap=1 → append реаллоцирует, a не тронут
// a == [1 2 3], b == [1 99]