❗️Осторожно: работа с append в GoЕсли вы пишете на Go, вы наверняка используете слайсы и функцию
append. Но знаете ли вы, что append может привести к неожиданным багам, если не понимать, как она работает? Давайте разберем, как избежать подводных камней.
❓Как работает append?Функция
append добавляет элементы в срез и возвращает новый срез. Однако есть нюанс: если емкость исходного среза достаточна, append изменит его. Если нет — создаст новый срез с новым базовым массивом.
go slice := []int{1, 2, 3}
newSlice := append(slice, 4)
slice[0] = 99
fmt.Println(newSlice)
Возможные проблемы:➡️ Если несколько срезов ссылаются на один базовый массив, изменение одного среза может повлиять на другие;
➡️ Если вы сохраняете ссылку на большой массив через под-срез, это может предотвратить сборку мусора для неиспользуемых данных;
➡️ Если вы предполагаете, что append всегда создает новый срез, это может привести к неожиданному поведению.
Как избежать проблем?1️⃣ Создавайте копии срезовЕсли вам нужно изменить срез, не затрагивая исходный, используйте функцию copy или явно создавайте новый срез.
go original := []int{1, 2, 3}
copied := make([]int, len(original))
copy(copied, original)
copied[0] = 99
fmt.Println(original)
fmt.Println(copied)
2️⃣ Используйте полное выражение срезаЕсли вы создаете под-срез и хотите контролировать его емкость, используйте полное выражение среза (
slice[low:high:max]).
go original := []int{1, 2, 3, 4, 5}
subslice := original[1:4:4]
subslice = append(subslice, 6)
fmt.Println(original)
fmt.Println(subslice)
3️⃣ Проверяйте емкость перед использованием appendЕсли вы хотите быть уверены, что append не изменит исходный срез, проверьте его емкость.
go slice := []int{1, 2, 3}
if cap(slice) == len(slice) {
newSlice := append(slice, 4)
fmt.Println(newSlice)
} else {
newSlice := append(slice, 4)
fmt.Println(newSlice)
}
🐸Библиотека Go разработчика