Собственные пакеты: структура папок, exported и unexported имена
Содержание курса
Импорт собственного пакета по пути модуля, блок import и циклическая зависимость
Путь импорта собственного пакета строится из двух частей: имя модуля из go.mod плюс путь подпапки относительно корня модуля. Если в go.mod написано module example.com/myapp, а пакет лежит в папке mathutil/, то импорт выглядит так:
import "example.com/myapp/mathutil"
После этого к exported-именам обращаются через имя пакета: mathutil.Add(2, 3). Имя, которое используется в коде — это именно package-имя из файла, не имя папки и не последний сегмент пути (хотя обычно они совпадают).
Когда нужно импортировать несколько пакетов, их группируют в блок:
import (
"fmt"
"example.com/myapp/mathutil"
)
Пустая строка между стандартными и собственными пакетами — общепринятое соглашение, goimports расставляет её автоматически. Если импортированный пакет нигде не используется, компилятор отказывается собирать программу — неиспользованный импорт запрещён.
Циклическая зависимость. Представим, что пакет api импортирует пакет storage, а storage решает импортировать api — например, чтобы использовать какой-то тип оттуда. Go-компилятор немедленно останавливается:
import cycle not allowed
Циклы запрещены полностью, без исключений. Решение зависит от причины цикла.
Если цикл возник из-за общего типа или константы, которая нужна обоим пакетам, — такой код выносят в третий пакет, например models, который не импортирует ни api, ни storage. Тогда оба пакета импортируют только models, и цикл исчезает:
api → models
↑
storage →
Этот приём работает именно тогда, когда общий код действительно независим и имеет смысл как отдельная единица.
В других ситуациях решение иное: иногда стоит пересмотреть направление зависимости — перенести ответственность в один из пакетов, чтобы второй больше не нуждался в обратном импорте. Иногда два тесно связанных пакета имеет смысл объединить в один. Универсального рецепта нет: цикл — это сигнал, что границы между пакетами стоит пересмотреть.
