Слайсы¶
TL;DR¶
- Slice =
struct{ ptr, len, cap }. Под капотом — массив. appendможет вернуть слайс с новым backing array, если cap не хватает.- Опасность: shared backing array.
- Правильно копировать:
copy(dst, src).
Анатомия¶
slice header:
+--------+--------+--------+
| ptr | len | cap |
+--------+--------+--------+
|
v
backing array: [ a | b | c | d | e | _ | _ ]
^
cap-граница
Создание:
s := []int{1, 2, 3} // len=3, cap=3
s := make([]int, 3) // len=3, cap=3
s := make([]int, 3, 10) // len=3, cap=10
append¶
s := make([]int, 0, 4)
s = append(s, 1, 2, 3, 4) // влезло в cap=4, без аллокации
s = append(s, 5) // переаллокация: cap → ~8
Когда cap исчерпан, runtime аллоцирует новый массив (обычно 2*cap для маленьких, x1.25 для больших), копирует данные, возвращает новый slice.
Shared backing — главная гочча¶
a := []int{1, 2, 3, 4, 5}
b := a[:3]
b = append(b, 99)
fmt.Println(a) // [1 2 3 99 5] — a[3] перезаписан!
fmt.Println(b) // [1 2 3 99]
b[:3] имеет cap=5 (унаследован от a). append пишет в a[3] потому что
есть место в cap. Если бы не было — был бы новый backing.
Правильно — отрезать cap руками:
b := a[:3:3] // полная форма: low:high:max → cap = max-low = 3
b = append(b, 99)
fmt.Println(a) // [1 2 3 4 5] — без сюрпризов
copy¶
src := []int{1, 2, 3}
dst := make([]int, len(src))
n := copy(dst, src) // вернёт min(len(dst), len(src))
Никогда не используй dst := src для глубокой копии — это копирует только
header, backing остаётся общим.
Передача в функцию¶
func mutate(s []int) { s[0] = 99 } // ✅ изменяет
func grow(s []int) { s = append(s, 9) } // ❌ изменения header'а теряются
Чтобы grow работал — возвращай новый slice:
📖 Тренируйся на задачах: must-solve-100 → C-001..C-004.