📜 Шпаргалка по Go: slices, maps, channels

Эта статья содержит основную информацию по таким структурам данных Go, как slices, maps и channels. Включает описание их поведения в разных состояниях и соответствующие примеры. Небольшое описание представлено для каждой структуры данных.

1. Slice (слайс)

Slice – структура данных, которая похожа на массив, но с возможностью изменения количества элементов. Слайс содержит ссылку на начало области данных: длину (количество элементов заданного типа) и емкость (capacity). Последнее представляет собой количество элементов в области памяти с первого элемента слайса и до конца этой области. Например, если в памяти 25 элементов и начало слайса указывает на 5-й элемент, то емкость будет – 20 (длина при этом может быть любой от 0 до 20, если больше, то произойдет выделение большего количества памяти и копирование имеющихся значений из старой области в эту новую, а также добавление новых элементов).

Поведение slice представлено в таблице:

состояние действие результат пример
nil чтение panic https://go.dev/play/p/Qs685lg2Erx
nil запись panic https://go.dev/play/p/W8A_zl0mZga
nil len 0 https://go.dev/play/p/2aqHrfcOD9L
nil cap 0 https://go.dev/play/p/F5pF3lXtsQa
nil append добавит элемент и вернет НЕ nil slice https://go.dev/play/p/9Iuc01njT4o
nil сравнение с nil true https://go.dev/play/p/9xMO8_9GZ6c
пустой чтение panic https://go.dev/play/p/XCFt5Dp7F6m
пустой запись panic https://go.dev/play/p/_XD_f_IoKVD
пустой len 0 https://go.dev/play/p/7pv8uwgBeNu
пустой cap 0 https://go.dev/play/p/EkKOleG9pRW
пустой сравнение с nil false https://go.dev/play/p/UuFyP_NLzIS
пустой append добавит элемент https://go.dev/play/p/Eo1o7udaP14
не пустой чтение вернет значение https://go.dev/play/p/8bmLymnJBHX
не пустой запись запишет значение https://go.dev/play/p/tU2ei00iyWE
не пустой len вернет длину https://go.dev/play/p/tyGUNUD1Eje
не пустой cap вернет емкость https://go.dev/play/p/5pP6Xo_eHfu
не пустой сравнение с nil false https://go.dev/play/p/QTgzq6f62VS
не пустой append добавит элемент https://go.dev/play/p/rhqMPn3Cees
Больше полезных материалов вы найдете на нашем телеграм-канале «Библиотека Go разработчика»

2. Map

Map структура данных, реализующая hash map. Hash Map позволяет по ключу получить значение за время O(1). На запись по ключу значения элемента также тратится O(1) времени.

Чтение элемента в Golang может осуществляться с помощью конструкции:

v, ok := someMap["someKey"]

Здесь ok возвращает true в случае, если элемент существует и прочитан и false, если нет. Если элемента нет, то всегда возвращается нулевое значение данного типа в качестве значения.

Поведение map представлено в таблице:

состояние действие результат пример
элемент есть чтение значение элемента, ok: true https://go.dev/play/p/3-KR5o2s_PP
элемент есть запись перезапишет существующее значение https://go.dev/play/p/Z2K9FuJd15e
элемента нет чтение значение: нулевое для данного типа, ok: false https://go.dev/play/p/gHy8zKFc8JK
элемента нет запись запишет новое значение по данному ключу https://go.dev/play/p/Y487vvnOD-y
nil чтение значение: нулевое для данного типа, ok: false https://go.dev/play/p/9ysg-MjuhXr
nil запись паника https://go.dev/play/p/VrXlQK1XaLd
nil len 0 https://go.dev/play/p/BJ3_cRC8ZSL
nil cap ошибка компиляции https://go.dev/play/p/siDXBNoNl5T
nil сравнение с nil true https://go.dev/play/p/zJDrAbVumih
инициализирована len количество значений в мапе https://go.dev/play/p/BQ-yb71aSaQ
инициализирована cap ошибка: invalid argument for cap https://go.dev/play/p/oLQKWp_hHxW
инициализирована сравнение с nil false https://go.dev/play/p/zTMnkuE2qEP

3. Channel

Channel – структура данных, которая используется для взаимодействия между горутинами. Горутина может послать сообщение (значение определенного типа данных) в канал, другая горутина может прочитать сообщение из канала. Каналы могут иметь или не иметь внутренний буфер.

В первом случае при записи в канал значения добавляются во внутренний буфер, а чтение тоже производится из него. Если буфер заполнен, писатель блокируется до того момента, пока не освободится место. Если буфер пустой, то блокируется читатель при попытке чтения до тех пор, пока в буфер не поступит информация.

В случае если буфера нет, писатель блокируется, пока читатель не будет готов прочесть, аналогично блокируется читатель, пока писатель не запишет информацию.

Оператор select позволяет читать из нескольких каналов. В каком канале появится первое сообщение, тот и будет прочитан и продолжится исполнение читателя.

Канал может иметь состояние «открыт» (тогда он работает как обычно – можно читать и писать) и «закрыт» (предполагается, что канал более не нужен и записывать больше ничего нельзя в такой канал).

Поведение небуферизованного канала:

состояние действие результат пример
открыт чтение (receive) если писатель готов, то чтение или ожидание информации в канале https://go.dev/play/p/-kZO7U3pEPY
открыт запись (send) если читатель готов, то запись или ожидание, пока не будет готов https://go.dev/play/p/gOJ8PP5ID6C
открыт select чтение из первого канала, где есть информация либо переход в default ветку (если объявлена) https://go.dev/play/p/qsQJixb1J4l
закрыт чтение (receive) чтение нулевого значения заданного типа (без ожидания) https://go.dev/play/p/mZ-a-uau1qP
закрыт запись (send) паника https://go.dev/play/p/i1M9adPQIiP
закрыт select чтение нулевого значения заданного типа (без ожидания) https://go.dev/play/p/TG-PwYPh3e_h
nil чтение (receive) читатель блокируется навсегда https://go.dev/play/p/_iXBO6_C3oC
nil запись (send) писатель блокируется навсегда https://go.dev/play/p/_iXBO6_C3oC
nil select пропускает канал https://go.dev/play/p/p5SfXeSezWW

Поведение буферизованного канала практически аналогичное, но с учетом наличия внутреннего буфера. В случае закрытия канала туда нельзя будет более записывать значения, но при чтении будут сначала прочитаны все, которые имеются в буфере, а после этого поведение будет как у небуферизированного – будут возвращаться нулевые значения соответствующего типа:

открыт чтение (receive) если есть информация в буфере, то чтение или ожидание появления информации https://go.dev/play/p/5vq_JEUH1de
открыт запись (send) если в буфере есть пустое место – запись, если нет – ожидание, пока не появится https://go.dev/play/p/CcQM-CQe2WE
открыт select чтение из первого канала, в буфере которого есть информация либо переход в default ветку (если объявлена) https://go.dev/play/p/p6BfZmwEmdd
закрыт чтение (receive) чтение всех значений из буфера, а после – чтение нулевого значения заданного типа (без ожидания) https://go.dev/play/p/3RFfAvLpWe2
закрыт запись (send) паника https://go.dev/play/p/XTL48XHMwNp
закрыт select чтение всех значений из буфера, а после – чтение нулевого значения заданного типа (без ожидания) https://go.dev/play/p/yVqQ-DnWd2B
nil чтение (receive) читатель блокируется навсегда https://go.dev/play/p/P6NzkZvgdAk
nil запись (send) писатель блокируется навсегда https://go.dev/play/p/P6NzkZvgdAk
nil select пропускает канал https://go.dev/play/p/N2G72DkTPFY

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

***

Материалы по теме

ЛУЧШИЕ СТАТЬИ ПО ТЕМЕ

Библиотека программиста
23 ноября 2018

Go vs Python: изучение основ языка Go в сравнении с Python

Это не соревнование двух языков, а просто еще один способ обучения. Рассмат...
admin
19 сентября 2018

TOП-3 языка программирования, которые нужно выучить до 2019

Это не просто три лучших языка программирования, а в некотором смысле попыт...