Пакеты в Go: инструкция без лишнего
Как устроена система пакетов в Go, зачем нужен go.mod, как создать свой пакет и импортировать чужой. Разбираем на рабочем проекте из двух пакетов — от init до go run.

Что получится и сколько времени займёт
За 20–30 минут вы создадите проект на Go с двумя собственными пакетами, научитесь их импортировать и подключите внешнюю зависимость. На выходе — запускающийся бинарник и понимание того, как Go находит код, почему имя папки важнее имени файла и куда складываются чужие модули.
Всё написано для Go 1.22+. Если у вас 1.18–1.21 — механика та же, но отдельные команды вроде go work могут вести себя иначе. Версию ниже 1.18 лучше обновить: модули там работают, но с оговорками.
Что нужно до старта
- Установленный Go 1.22+ (
go versionв терминале должен вернуть версию). - Любой текстовый редактор. VS Code с расширением Go или GoLand удобнее, но не обязательны.
- Терминал: bash, zsh, PowerShell — без разницы.
- Доступ в интернет — понадобится для загрузки внешнего пакета.
Шаг 1. Инициализируем модуль
Модуль — это корень вашего проекта. Внутри него живут пакеты. Без модуля Go не знает, как разрешать импорты.
mkdir myapp
cd myapp
go mod init example.com/myapp
Путь example.com/myapp — это имя модуля. Если проект на GitHub, ставьте реальный путь: github.com/yourname/myapp. Для локальных экспериментов подойдёт любой.
Проверка: в папке появился файл go.mod с содержимым вроде:
module example.com/myapp
go 1.22
Шаг 2. Пишем main-пакет
Каждая программа на Go начинается с пакета main и функции main(). Создайте файл main.go в корне проекта.
// main.go
package main
import "fmt"
func main() {
fmt.Println("Приложение запущено")
}
Проверка: выполните go run . — в терминале появится строка «Приложение запущено».
Шаг 3. Создаём свой пакет
Пакет в Go — это папка с .go-файлами, у которых одинаковое значение package. Имя папки и имя пакета принято делать одинаковыми — это не формальное требование компилятора, но стандартная конвенция, нарушение которой запутает любого читателя кода.
mkdir greet
Создайте файл greet/greet.go:
// greet/greet.go
package greet
import "fmt"
// Hello выводит приветствие по имени.
// Функция начинается с заглавной буквы — значит, она экспортируется.
func Hello(name string) string {
return fmt.Sprintf("Привет, %s!", name)
}
Экспорт через регистр
В Go нет ключевых слов public и private. Всё, что начинается с заглавной буквы, доступно за пределами пакета. Строчная буква — только внутри. Это единственный механизм видимости, и он работает для функций, типов, переменных и констант одинаково.
Шаг 4. Импортируем свой пакет
Вернитесь в main.go и подключите пакет greet. Путь импорта — это имя модуля плюс относительный путь к папке пакета.
// main.go
package main
import (
"fmt"
"example.com/myapp/greet"
)
func main() {
msg := greet.Hello("Мир")
fmt.Println(msg)
}
Проверка: go run . выведет «Привет, Мир!».
Шаг 5. Добавляем второй пакет
Реальный проект — это не один пакет. Создадим calc:
mkdir calc
// calc/calc.go
package calc
// Sum возвращает сумму двух целых чисел.
func Sum(a, b int) int {
return a + b
}
Обновите main.go:
// main.go
package main
import (
"fmt"
"example.com/myapp/calc"
"example.com/myapp/greet"
)
func main() {
fmt.Println(greet.Hello("Мир"))
fmt.Println("2 + 3 =", calc.Sum(2, 3))
}
Проверка: go run . выведет две строки — приветствие и результат сложения.
Шаг 6. Подключаем внешнюю зависимость
Внешний пакет подключается одной командой. Для примера возьмём популярный роутер:
go get github.com/go-chi/chi/v5
После выполнения в go.mod появится строка require, а рядом создастся файл go.sum с хешами. Оба файла коммитятся в репозиторий.
Проверка: откройте go.mod — там есть строка require github.com/go-chi/chi/v5 v5.x.x с конкретной версией.
Структура проекта после всех шагов
| Путь | Что это |
|---|---|
go.mod | Описание модуля и зависимостей |
go.sum | Хеши загруженных модулей |
main.go | Точка входа, пакет main |
greet/greet.go | Пакет с функцией приветствия |
calc/calc.go | Пакет с арифметикой |
Ключевые правила, которые стоит запомнить
- Одна папка — один пакет. Нельзя положить в одну папку файлы с разными значениями
package(исключение — файлы с суффиксом_test.goи пакетомpackage foo_test). - Имя импорта — путь от корня модуля, а не от
GOPATH.GOPATH-режим без модулей считается устаревшим с Go 1.16. - Циклические импорты запрещены. Если пакет A импортирует B, то B не может импортировать A — компилятор выдаст ошибку
import cycle not allowed. Решение — вынести общие типы в третий пакет. - Пакет
internal— способ ограничить видимость: код внутри папкиinternalдоступен только родительскому модулю и его подпакетам.
Пакет в Go — единица компиляции и единица интерфейса одновременно. Если вы проектируете пакет так, чтобы у него был узкий и понятный API из экспортированных имён, остальное приложение можно менять без страха.
Если не получилось
«package example.com/myapp/greet is not in std»
Go ищет пакет в стандартной библиотеке и не находит. Причина — вы запустили go run вне папки модуля или забыли go mod init. Убедитесь, что go.mod лежит в корне проекта и вы находитесь в этой папке.
«cannot find module providing package …»
Путь импорта не совпадает с тем, что записан в go.mod. Сравните строку module в go.mod и строку импорта в main.go — они должны совпадать посимвольно, включая регистр.
«greet.hello undefined (type … has no field or method hello)»
Функция написана со строчной буквы — hello вместо Hello. Go не экспортирует имена, начинающиеся с маленькой буквы. Переименуйте функцию.
«import cycle not allowed»
Два пакета импортируют друг друга. Вынесите общий тип или интерфейс в третий пакет, который не импортирует ни один из двух.
«go.sum is out of sync»
Выполните go mod tidy — команда пересчитает зависимости и обновит go.sum.
Сноска: Модуль — единица версионирования и распространения кода в Go. Один модуль содержит один go.mod и может включать произвольное количество пакетов. Пакет — папка с исходниками, модуль — весь проект целиком. Разница важна: go get загружает модули, а import ссылается на пакеты внутри них.
Что делать дальше
- Добавьте тесты: создайте
greet/greet_test.goс функциейTestHelloи запуститеgo test ./.... - Изучите
go mod tidy,go mod vendorиgo mod graph— они понадобятся, как только зависимостей станет больше одной. - Посмотрите, как работает директива
replaceвgo.mod— она пригодится, если вы редактируете форк чужой библиотеки локально. - Прочитайте раздел «Managing dependencies» в официальной документации Go — там описаны нюансы версионирования, с которыми сталкивается любой проект крупнее учебного.
Редактор изображений: Убрать лишнее, заменить фон, добавить объект, расширить кадр — на вашем изображении.
Спросить за 20 ₽Источники: Go Modules Reference — официальная документация, Tutorial: Create a Go module — go.dev
Частые вопросы
Чем модуль отличается от пакета?
Обязательно ли имя папки и имя пакета должны совпадать?
Можно ли положить два пакета в одну папку?
Что если у меня Go версии 1.15 или ниже?
Как решить циклический импорт, если оба пакета зависят друг от друга?
Куда Go скачивает внешние зависимости?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.