+1 098новых вакансий за сутки92 474вакансии открыты сейчасНовое:Talanto осенью: фильтры с числами, отклик по шагам и каталог компаний
Когда append превышает cap, Go выделяет новый массив большего размера, копирует данные и возвращает новый slice-заголовок. Исходный slice остаётся неизменным. Если cap не превышена, слайсы делят один backing array.
package main
import "fmt"
func main() {
s := make([]int, 3, 3) // len=3, cap=3
s[0], s[1], s[2] = 1, 2, 3
// append превышает cap=3 -> выделяется новый массив
s2 := append(s, 4)
fmt.Printf("s: ptr=%p len=%d cap=%d val=%v\n", &s[0], len(s), cap(s), s)
fmt.Printf("s2: ptr=%p len=%d cap=%d val=%v\n", &s2[0], len(s2), cap(s2), s2)
// s2 указывает на ДРУГОЙ массив!
s2[0] = 99
fmt.Println("s[0]:", s[0]) // 1 — s не изменился
fmt.Println("s2[0]:", s2[0]) // 99
}
Когда вы вызываете append и новый элемент не помещается в текущую ёмкость (len == cap), рантайм Go выполняет следующие шаги:
Исходный slice остаётся неизменным и указывает на старый массив.
До Go 1.18: если cap < 1024, ёмкость удваивается; иначе растёт на 25%.
С Go 1.18+: используется плавная формула, которая постепенно уменьшает коэффициент роста по мере увеличения slice, что снижает потребление памяти.
a := make([]int, 3, 5) // len=3, cap=5 — есть запас
a[0], a[1], a[2] = 1, 2, 3
b := append(a, 4) // cap не превышена — b и a ДЕЛЯТ массив!
b[0] = 99
fmt.Println(a[0]) // 99! Мутация b изменила a
Это классическая ловушка: если после append не было перевыделения, слайсы делят один и тот же backing array.
// Явно ограничить cap при нарезке
sub := original[1:3:3] // len=2, cap=2 (third index)
// теперь append на sub всегда выделит новый массив
// Или явно скопировать
copy := append([]int{}, original...)
// Если знаете итоговый размер — выделяйте заранее
result := make([]int, 0, len(input))
for _, v := range input {
result = append(result, transform(v))
}
// Ноль перевыделений
append: append(s, x) без присваивания — наиболее частая ошибка; Go не ругается, но данные теряются.a[lo:hi:max] существует именно для ограничения cap, но редко используется на практике, что приводит к неожиданным разделениям.testing?go.mod?