Eugene Mikhalev 31 марта 2023

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

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

1. Slice (слайс)

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

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

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

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

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

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

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

***

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

МЕРОПРИЯТИЯ

У вас остались какие-нибудь вопросы?

ВАКАНСИИ

Добавить вакансию
Backend Lead (Python, Django)
по итогам собеседования
DevOps
от 350000 RUB

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