Массивы и срезы: создание, индексирование, len, cap, append и range
Содержание курса
append с обязательным присваиванием и for range по срезу
append — единственный стандартный способ добавить элемент в срез. Он принимает срез и один или несколько новых значений, а возвращает обновлённый срез:
s := []int{1, 2, 3}
s = append(s, 4)
fmt.Println(s) // [1 2 3 4]
Обычная форма в этом уроке — s = append(s, v). Главное правило: используйте возвращённый срез. Его длина увеличена на число добавленных элементов, а базовый массив мог сохраниться или смениться — это зависит от текущей ёмкости и остаётся деталью реализации.
Результат append можно также присвоить другой переменной, вернуть из функции или сразу передать следующему вызову. Что недопустимо — полностью игнорировать возвращённое значение:
append(s, 4) // ошибка компиляции: append(s, 4) evaluated but not used
Программа не соберётся. Go требует, чтобы возвращённый срез был использован.
append отлично работает с nil-срезом — вы видели его в предыдущей секции:
var s []int
s = append(s, 10, 20)
fmt.Println(s) // [10 20]
Для перебора среза используйте цикл for range:
s := []string{"go", "is", "fun"}
for i, v := range s {
fmt.Println(i, v)
}
// 0 go
// 1 is
// 2 fun
i — индекс, v — копия элемента. Это важно. Если написать:
for _, v := range s {
v = "changed" // не меняет s
}
— срез останется нетронутым. v — локальная переменная итерации, изменение которой не отражается на оригинале. Чтобы изменить элемент, нужно присваивание по индексу:
for i := range s {
s[i] = "changed"
}
fmt.Println(s) // [changed changed changed]
Форма for i := range s даёт только индекс — используйте её, когда значение элемента в теле цикла не нужно. Если нужно только значение без индекса, отбросьте индекс через _:
for _, v := range s {
fmt.Println(v)
}
Самопроверка: append не гарантирует отдельный массив
Создайте срез и его подсрез, измените элемент подсреза и посмотрите на исходный. Затем добавьте элемент через append. Если capacity хватает, добавление может использовать прежний базовый массив, а при нехватке памяти выделяется новый. Поэтому вывод «после append срезы всегда независимы» неверен.
Для копирования создайте целевой срез подходящей длины и вызовите copy. Проверьте именно length, а не только capacity: copy записывает не больше минимальной длины источника и назначения. Указатель на общий массив объясняет поведение лучше, чем запоминание отдельных примеров. Разберите это до задач, где функция изменяет полученный slice.
Продолжите разбор: Разделяемый базовый массив, copy и подсрезы.
