В Go встроенные функции append, len и cap играют ключевую роль при работе со срезами (slices).
Пример:
arr := []int{1,2,3} arr2 := append(arr, 4) // arr2 может быть на том же или на новом backing array, в зависимости от cap(arr)
Тонкости:
Что произойдёт, если срезу "отрезать" кусок и добавить элемент через append? Повлияет ли это на исходный массив?
Ответ: Если cap позволяет, append запишет элемент "в хвост" исходного массива, и изменения видны через все срезы, которые ссылаются на тот же массив.
Пример:
a := []int{1,2,3,4} b := a[:2] // [1 2], len=2, cap=4 b = append(b, 10) // меняется a: a -> [1, 2, 10, 4]
История
В команде добавили элементы в дочерний срез, и неожиданно изменились данные в родительском массиве — это вызвало рассогласование бизнес-логики и сложнейшие для дебага баги в распределении задач между пользователями.
История
При повторном вызове append на большом срезе ожидали, что новый массив всегда будет выделяться заново, но по факту несколько частей системы продолжали работать с одним и тем же "backing array", провоцируя race condition и повреждение данных.
История
Разработчик выделял срез фиксированного размера с помощью make, но допустил ошибку и поменял местами аргументы: make([]int, cap, len). В результате логика, ориентированная на вместимость, неожиданно работала на длину, что спровоцировало панику при выходе за пределы s[0:len].