Заголовок слайса (Slice header)
Магия, благодаря которой срез ведет себя и как значение, и как указатель, заключается в том, что срез на самом деле является структурным типом. Его обычно называют заголовком слайса (Slice header) из-за его аналога в пакете reflect. Определение заголовка слайса выглядит примерно так:
Подробнее об аналоге в пакете reflect ты можешь узнать по ссылке.
Размер структуры составляет 24 байта со следующими полями:
-
data он же ptr на схеме. Тип на схеме неверный, верный — uintptr, являющийся алиасом на uint, указатель на начало данных, условно — на номер начального байта данных.
-
len (length — длина) c типом int, хранит в себе текущее количество элементов в слайсе.
-
Capacity (capacity — емкость) с типом int, хранит в себе возможное количество элементов. Связано с памятью, при превышении этого порога, выделяется новая память под внутренний массив, на которой указывает значение поля data, а значит и значение в поле data изменится.
Слайсы, в отличие от map и chan, являются типами значений и копируются при назначении или передаче в качестве аргументов функциям.
Чтобы проиллюстрировать это, программисты инстинктивно понимают, что в функции square аргумент v является независимой копией переменной v, объявленной в main. То есть как раннее говорилось в главах про функции, все аргументы копируются в стек функции.
Самое время протестировать код!
Таким образом, операция square не влияет на переменную v в main. Точно так же и аргумент функции является независимой копией среза s, объявленного в main, а не указателем на значение в функции main.
Самое время протестировать код!
Немного необычный характер переменной среза Go заключается в том, что она передается как значение, а не как указатель. В 90% случаев, когда вы объявляете структуру в Go, вы будете передавать указатель на значения этой структуры. Если в этой структуре определен метод и/или она используется для удовлетворения интерфейса, то со сто процентной вероятностью вы будете передавать по указателю на вашу структуру.
Именно это исключительное поведение срезов как значений, а не указателей на значения, может запутать программиста Go в понимании того, как работают слайсы. Просто помните, что всякий раз, когда вы назначаете, разделяете, передаете или возвращаете слайс, вы делаете копию трех полей в заголовке слайса; указатель на базовый массив, а также текущую длину и емкость. Далее мы подробнее разберем эти моменты в объяснении функции append.