Какие типы являются ссылочными в Go
Что проверяют
Понимаете ли вы, что в Go всё передаётся по значению, а «ссылочность» некоторых типов объясняется тем, что лежит внутри этого значения. Следом обычно дают задачку: срез передали в функцию, там сделали append, что увидит вызывающий код.
Как отвечать
Термина «ссылочный тип» в спецификации Go нет, его оттуда давно убрали. Сильный ответ звучит так: копируется всегда значение, но у части типов оно само указывает на общие данные:
- *указатель `T`**: адрес, копия указателя смотрит на тот же объект;
- срез: заголовок из указателя на массив,
lenиcap. Копия делит массив с оригиналом, ноlenиcapу неё свои; - map: переменная хранит указатель на структуру рантайма, поэтому изменения видны через все копии;
- канал: тоже указатель на структуру рантайма с буфером и очередями ждущих горутин;
- функция и интерфейс: функция ссылается на код и захваченные переменные, интерфейс состоит из двух слов, типа и указателя на данные.
Массивы, структуры и строки ведут себя как значения: присваивание массива копирует все элементы, а строки неизменяемы, так что общий буфер у них незаметен.
Главная ловушка срезов: если append укладывается в cap, запись идёт в общий массив и может затереть данные соседнего среза; если не укладывается, выделяется новый массив и связь рвётся. Поэтому функция, меняющая длину, возвращает новый срез, как делает сам append. У map своя ловушка: чтение из nil-map безопасно, а запись вызывает панику, так что map создают через make или литерал.
Где спотыкаются
Называют структуру ссылочным типом, если её передали по указателю. Ссылочным тут является указатель, а структура остаётся значением и при присваивании копируется целиком.
Утверждают, что изменения в срезе всегда видны во всех копиях. Это верно для записи по индексу в пределах общего массива, после перераспределения в append копии живут отдельно.
Думают, что внутри функции m = make(map[string]int) или s = nil изменит переменную вызывающего кода. Присваивается локальная копия, снаружи ничего не поменяется.