Пост из канала Thank Go!

Вроде бы запомнили, что строки в Го — это слайсы байт. Но следует признаться, что я вас обманул, и все не так просто.

На самом деле слайс в Го — это простая структура, содержащая только указатель на массив, длину и ёмкость слайса. Никаких данных в самом слайсе нет, они лежат в массиве. Вот тут было сложно, поэтому кратко по порядку.

Массив

Структура индексированных элементов фиксированного размера. Массив всегда передаётся по значению, а значение его — это весь массив целиком, а не ссылка на первый элемент. Инициализированный массив уже содержат значения по умолчанию его элементов.

var a [1]int
fmt.Println(a[0])
// >> 0

Слайс

Чтобы постоянно не копировать все элементы массива в Го придуман слайс — абстрактный тип над массивом, состоящий из:

  • ссылки на массив,
  • размера слайса — количество инициализированных элементов,
  • ёмкость слайса — размер, до которого можно увеличить слайс, без пересоздания массива. Когда превышается ёмкость, в памяти выделяется массив большей длины, в него копируется старый, а в слайсе обновляется ссылка.

Слайс передается через копирование значения. Но загвоздка в том, что значение это содержит лишь ссылку. Тут появляется магия.

var a = []int{1, 2, 3}
func(in []int) {
	in[0] = 10
}(a)
fmt.Println(a[0])
// >> 10

Опа, изменился элемент внешнего слайса! Внутри функции мы изменили элемент, хранящийся в массиве, ссылка на который была передана внутрь функции внутри слайса.

Добавим новый элемент в слайс после изменения

var a = []int{1, 2, 3}
func(in []int) {
	in[0] = 10
	in = append(in, 4)
}(a)
fmt.Println(a[0])
// >> 10

Все вроде логично. А если добавить элемент перед изменением?

var a = []int{1, 2, 3}
func(in []int) {
	in = append(in, 4)
	in[0] = 10
}(a)
fmt.Println(a[0])
// >> 1

Заметили магию? Элемент внешнего слайса не поменял значение! Потому что в скоупе функции мы первым делом добавляем элемент в слайс, превышая его ёмкость. Из-за этого происходит выделение нового массива, элемент которого мы и изменяем на втором шаге. Но внешний слайс ничего об этом не знает.

Как вам такое? Поиграться с примерами в плейграунде

А о строках, как слайсах байт, мы поговорим в следующем посте.