Skip to content

Заголовок слайса (Slice header)

Магия, благодаря которой срез ведет себя и как значение, и как указатель, заключается в том, что срез на самом деле является структурным типом. Его обычно называют заголовком слайса (Slice header) из-за его аналога в пакете reflect. Определение заголовка слайса выглядит примерно так:

Подробнее об аналоге в пакете reflect ты можешь узнать по ссылке.

Размер структуры составляет 24 байта со следующими полями:

  1. data он же ptr на схеме. Тип на схеме неверный, верный — uintptr, являющийся алиасом на uint, указатель на начало данных, условно — на номер начального байта данных.

  2. len (length — длина) c типом int, хранит в себе текущее количество элементов в слайсе.

  3. Capacity (capacity — емкость) с типом int, хранит в себе возможное количество элементов. Связано с памятью, при превышении этого порога, выделяется новая память под внутренний массив, на которой указывает значение поля data, а значит и значение в поле data изменится.

Слайсы, в отличие от map и chan, являются типами значений и копируются при назначении или передаче в качестве аргументов функциям.

Чтобы проиллюстрировать это, программисты инстинктивно понимают, что в функции square аргумент v является независимой копией переменной v, объявленной в main. То есть как раннее говорилось в главах про функции, все аргументы копируются в стек функции.

Самое время протестировать код!

Таким образом, операция square не влияет на переменную v в main. Точно так же и аргумент функции является независимой копией среза s, объявленного в main, а не указателем на значение в функции main.

Самое время протестировать код!

Немного необычный характер переменной среза Go заключается в том, что она передается как значение, а не как указатель. В 90% случаев, когда вы объявляете структуру в Go, вы будете передавать указатель на значения этой структуры. Если в этой структуре определен метод и/или она используется для удовлетворения интерфейса, то со сто процентной вероятностью вы будете передавать по указателю на вашу структуру.

Именно это исключительное поведение срезов как значений, а не указателей на значения, может запутать программиста Go в понимании того, как работают слайсы. Просто помните, что всякий раз, когда вы назначаете, разделяете, передаете или возвращаете слайс, вы делаете копию трех полей в заголовке слайса; указатель на базовый массив, а также текущую длину и емкость. Далее мы подробнее разберем эти моменты в объяснении функции append.