Go: когда append() меняет не тот слайс🍕

Разбирался с поведением слайсов в Go и наткнулся на интересный нюанс. Написал небольшой пример: добавил элемент в один слайс, изменил его значение и посмотрел, что осталось в исходном.

original := make([]int, 2, 4) original[0] = 10 original[1] = 20

derived := append(original, 30) derived[0] = 999

fmt.Println(original) // [999 20] fmt.Println(derived) // [999 20 30]

Неочевидный момент: я изменил derived[0], но значение в original тоже поменялось. Почему? Оказалось, всё дело в ёмкости слайса. У original она равна четырём, хотя элементов всего два. Поэтому append() использовал тот же внутренний массив, а не выделил новый. Оба слайса оказались связаны с одной областью памяти.

Решил проверить, что будет, если ёмкости не хватит: original := make([]int, 2, 2) original[0] = 10 original[1] = 20

derived := append(original, 30) derived[0] = 999

fmt.Println(original) // [10 20] fmt.Println(derived) // [999 20 30]

Теперь исходный слайс остался без изменений. Для добавления элемента потребовался новый массив, и derived стал использовать его.

Какой вывод? append() не гарантирует создание независимого массива. Всё зависит от доступной ёмкости. Если нужна отдельная копия, можно использовать slices.Clone(): derived := slices.Clone(original) После этого изменения элементов derived не затронут элементы original для обычных слайсов с независимыми значениями элементов. Небольшой эксперимент, но хорошо показывает, почему в Go важно понимать не только len, но и cap. А вы сталкивались с подобными побочными эффектами при работе со слайсами?