Ответ
Функция append добавляет один или несколько элементов в конец слайса и возвращает новый слайс. Её работа зависит от ёмкости (capacity) исходного слайса.
Алгоритм работы:
-
Проверка ёмкости: Go проверяет, достаточно ли места в нижележащем массиве (underlying array) для добавления новых элементов. Это сравнивается
len(slice) + len(new_elements)сcap(slice). -
Если ёмкости достаточно (
cap>len):- Новые элементы добавляются в существующий нижележащий массив сразу после последнего элемента.
- Функция возвращает новый заголовок слайса, который указывает на тот же массив, но имеет обновленную длину (
len).
-
Если ёмкости недостаточно (
cap==len):- Go выделяет новый, больший массив.
- Стратегия роста: для небольших слайсов ёмкость обычно удваивается. Для слайсов размером более 1024 элементов рост замедляется до ~1.25x, чтобы избежать избыточного расхода памяти.
- Все элементы из старого массива копируются в новый.
- Новые элементы добавляются в конец нового массива.
- Функция возвращает заголовок слайса, указывающий на этот новый массив.
Важный нюанс: append и нижележащий массив
Поскольку append может изменять существующий массив, это может приводить к неожиданному поведению, если несколько слайсов ссылаются на один и тот же массив.
package main
import "fmt"
func main() {
// Создаем массив и два слайса, указывающих на него
sourceArray := [5]int{10, 20, 30, 40, 50}
slice1 := sourceArray[0:2] // {10, 20}, len=2, cap=5
slice2 := sourceArray[1:3] // {20, 30}, len=2, cap=4
fmt.Printf("До: slice1=%v, slice2=%v, array=%vn", slice1, slice2, sourceArray)
// append для slice1 имеет достаточно ёмкости в sourceArray
slice1 = append(slice1, 99)
// Так как slice1 изменил нижележащий массив, slice2 тоже "увидел" это изменение
fmt.Printf("После: slice1=%v, slice2=%v, array=%vn", slice1, slice2, sourceArray)
// Вывод:
// До: slice1=[10 20], slice2=[20 30], array=[10 20 30 40 50]
// После: slice1=[10 20 99], slice2=[20 99], array=[10 20 99 40 50]
}
Золотое правило: Всегда присваивайте результат append обратно в переменную слайса, так как функция может вернуть как измененный старый, так и совершенно новый слайс.
slice = append(slice, element)
Ответ 18+ 🔞
А, блядь, про append в Go! Ну это ж классика, ёпта! Сидишь такой, думаешь — ну добавил элемент в слайс, и что тут такого? А там, сука, под капотом целый ебаный цирк с конями!
Смотри, алгоритм-то вроде простой, но с подвохом, как сука в бане с мокрым полом.
Шаг первый: Go смотрит, хватит ли места в этом самом «нижележащем массиве» — там, где реально данные лежат. Сравнивает: текущая длина + сколько новых элементов хочешь запихнуть vs ёмкость (capacity).
Если места хватает — ну, просто пиздуй элементы в конец, в свободные ячейки. Возвращает тот же слайс, но с обновлённой длиной. Красота!
А вот если не хватает — тут начинается пиздец. Go такой: «Опа, братан, тесно тут!» — и выделяет НОВЫЙ массив, побольше. Для мелких слайсов ёмкость обычно удваивают, а для больших (от 1024) — умножают на 1.25, чтоб память не жрать как свинья. Потом старые данные копирует в новый массив, новые элементы прилепляет следом и возвращает слайс, который уже смотрит на этот новый массив.
А теперь, внимание, самый сок, где все и ебутся!
package main
import "fmt"
func main() {
sourceArray := [5]int{10, 20, 30, 40, 50}
slice1 := sourceArray[0:2] // {10, 20}, len=2, cap=5
slice2 := sourceArray[1:3] // {20, 30}, len=2, cap=4
fmt.Printf("До: slice1=%v, slice2=%v, array=%vn", slice1, slice2, sourceArray)
// Тут у slice1 ёмкости хватает — он не создаёт новый массив, а лепит 99 в старый!
slice1 = append(slice1, 99)
// И slice2, который смотрит на тот же массив, внезапно видит: "Бля, а откуда тут 99 взялся?!"
fmt.Printf("После: slice1=%v, slice2=%v, array=%vn", slice1, slice2, sourceArray)
// Вывод:
// До: slice1=[10 20], slice2=[20 30], array=[10 20 30 40 50]
// После: slice1=[10 20 99], slice2=[20 99], array=[10 20 99 40 50]
}
Видишь? slice2 нихуя не ожидал, что его данные поменяются! А всё потому, что оба слайса делили один массив, и append вписал прямо в него.
Золотое правило, блядь, выучи как «Отче наш»: Всегда присваивай результат append обратно в переменную! Потому что функция может вернуть и старый слайс (если массив не менялся), и новый (если пришлось переехать). Не сделаешь — останешься с хуем в руках и непонятным багом.
slice = append(slice, element) — запомни эту мантру, и будет тебе счастье. А то потом будешь искать, почему данные поехали, а оно вот где собака зарыта, сука!