Как работает функция append со слайсами в Go
Что проверяют
Понимаете ли вы, что append может как изменить исходный массив, так и создать новый, и знаете ли следствия.
Как отвечать
append добавляет элементы в конец и возвращает слайс. Логика такая:
- хватает ёмкости: элементы записываются в существующий массив, возвращается слайс с той же основой и увеличенной длиной;
- не хватает: выделяется новый массив (примерно вдвое больше для небольших слайсов и с меньшим коэффициентом для крупных), данные копируются, возвращается слайс поверх нового массива.
Отсюда правило: результат обязательно присваивают обратно, s = append(s, v). Функция не может изменить длину слайса вызывающего, потому что описатель передаётся по значению.
Главная ловушка следует из первого случая: пока ёмкости хватает, запись идёт в общий массив, и её видят все, кто на него ссылается.
a := make([]int, 3, 10)
b := a[:2]
b = append(b, 99) // записалось в общий массив
fmt.Println(a[2]) // 99, хотя a не трогалиЗащита от этого это полная форма среза a[low:high:max], ограничивающая ёмкость: тогда append гарантированно выделит новый массив.
Ещё детали: append(s, other...) добавляет содержимое другого слайса, append([]byte, "строка"...) работает для байтов, а append(s) без аргументов просто возвращает тот же слайс.
Где спотыкаются
Не присваивают результат.
Не знают про общий массив и получают неожиданно изменившиеся данные.
Считают, что append всегда копирует.
Не задают ёмкость заранее и получают цепочку перевыделений в цикле.