---
title: "Структура программы: package, import, func main"
description: "Структура программы на Go: package main, import и func main. Разбираем точку входа, правила компиляции, экспорт имён и типичные ошибки новичков с примерами."
course: "Go с нуля: базовый синтаксис и конструкции"
url: "https://golangmaster.ru/courses/go-basics/lessons/03-struktura-programmy/"
published: "2026-09-19"
updated: "2026-09-28"
minutes: 18
---

# Структура программы: package, import, func main

*Курс «Go с нуля: базовый синтаксис и конструкции», урок 3 из 25. ~18 минут. Обновлено 28.09.2026. Источник: https://golangmaster.ru/courses/go-basics/lessons/03-struktura-programmy/*

Структура программы на Go собирается по одной схеме: package сверху, import ниже, func main как точка входа. Схема строгая до занудства, и в этом скрыта хорошая новость: прочитав этот урок, вы сможете разобрать скелет любой программы на языке — от учебного примера до исходников Kubernetes. Разберём каждый слой, правила компиляции и ошибки, которые новички ловят первыми.

Откройте любой Go-файл — из документации, из открытого проекта на GitHub, из этого курса — и первые строки будут один в один: объявление пакета, затем импорты. Это не привычка сообщества, а часть языка: компилятор не начнёт сборку, если файл не объявил пакет, откажется работать с неиспользованным импортом и не соберёт программу без функции main. К такой строгости привыкаешь быстрее, чем кажется. Сначала она мешает — рука тянется просто стереть ненужную строку и пойти дальше; зато потом, читая чужой код, вы всегда знаете, где смотреть: что этот файл подключает и с какой функции начинается выполнение.

### package: объявление пакета

Первая строка любого файла .go — package имя. Пакет — единица компиляции и пространство имён: все файлы одной папки обязаны объявлять один и тот же пакет, иначе компилятор соберёт их в одну кучу неправильно и пожалуется. Специальный случай — package main: так помечаются программы, а не библиотеки. Команда go build собирает main-пакет в исполняемый файл; любой другой пакет становится библиотекой, из которой чужие программы импортируют функции. Отсюда правило, которое ловит всех новичков: package main живёт только там, где вы хотите получить запускаемую программу. Названия пакетов короткие, в нижнем регистре, без подчёркиваний: main, fmt, strings, http. Имя пакета совпадает с именем папки (main — исключение), и по этому имени вы обращаетесь к содержимому: fmt.Println — это пакет fmt, точка, функция Println из него.

### Минимальная программа целиком

```go
package main

import "fmt"

func main() {
        fmt.Println("Скелет программы")
}
```

Три слоя: объявление пакета, импорт стандартной библиотеки, точка входа. В файле больше ничего нет — и это уже полноценная программа, которую собирает любой компилятор Go.

### import: подключение пакетов

Секция import перечисляет пакеты, чьи функции используются в файле. Один пакет — одна строка в кавычках; если пакетов несколько, их группируют в скобках — так принято в стандартной библиотеке и во всех крупных проектах. Содержимое пакета вызывается через точку: strings.ToUpper, fmt.Println. Путь в кавычках для стандартной библиотеки — просто имя пакета, а для чужого кода будет длиннее, с доменом проекта, — к модулям вернёмся в продвинутом курсе. Теперь неприятная часть, о которой лучше узнать заранее: компилятор требует, чтобы каждый импорт был использован. Ни предупреждений, ни «потом уберу» — неиспользованный импорт останавливает сборку. Точно так же он поступает с неиспользованными переменными. Это раздражает ровно до первого раза, когда вам достаётся чужой рефакторинг на две тысячи строк, — и в нём нет ни одного мёртвого импорта.

### Групповой импорт

```go
package main

import (
        "fmt"
        "strings"
)

func main() {
        fmt.Println(strings.ToUpper("imports работают"))
}
```

Два пакета в одной секции import. Из fmt берём печать, из strings — функцию ToUpper, которая поднимает регистр строки. Пакет используется через имя: strings.ToUpper.

> **imported and not used.** Удалите из примера вызов strings.ToUpper — и компиляция упадёт с сообщением «imported and not used: strings». Это ошибка, а не предупреждение: сборка не пройдёт, пока мёртвый импорт не убран. Хороший редактор подчёркивает такие строки ещё до запуска.

### func main: точка входа

Функция main — место, где программа начинается. Три правила. Первое: у main нет параметров и она ничего не возвращает; аргументы командной строки достаются иначе, через пакет os, о котором поговорим в финальном проекте. Второе: main обязана лежать в package main — точка входа в библиотеке бессмысленна, библиотеку запускает чужая программа. Третье, самое важное для будущего: программа живёт ровно до конца main. Как только main завершается, закрывается всё приложение, включая запущенные фоновые задачи. Запомните это — когда через двадцать уроков дойдём до горутин, именно эта строка объяснит половину их багов. И ещё одна приятность: компилятору всё равно, в каком порядке функции объявлены внутри пакета. Вызвать функцию, написанную ниже по файлу, можно спокойно.

### Порядок объявления функций не важен

Запустите код. Потом перепишите функции в другом порядке — вывод не изменится. Затем удалите вызов finish() и запустите снова: функция стала мёртвым кодом, но ошибки не будет. Мёртвая функция разрешена, мёртвый импорт — нет: компилятор разумен в строгости.

```go
package main

import "fmt"

func main() {
        fmt.Println("start: программа стартовала")
        greet()
        finish()
        fmt.Println("start: программа завершена")
}

func greet() {
        fmt.Println("greet: привет из другой функции пакета")
}

func finish() {
        fmt.Println("finish: завершаем")
}
```

### Что выведет программа?

```go
package main

import "fmt"

func main() {
        finish()
        start()
}

func start() { fmt.Println("старт") }
func finish() { fmt.Println("финиш") }
```

**Что выведет программа?**

- «старт», затем «финиш» — в порядке объявления функций
- ✅ «финиш», затем «старт» — в порядке вызова из main
- Ошибка: start объявлена после вызова
- Ничего: main пустая

Компилятору безразличен порядок объявления функций в пакете — выполняется то, что вызвано из main, в порядке вызова. finish() идёт первой строкой, поэтому «финиш» печатается раньше.

### Экспортируемое и внутреннее: заглавная буква решает

В других языках есть ключевые слова public, private и protected. В Go роль модификатора доступа играет первая буква имени. Println начинается с заглавной — значит, функция экспортируемая, видна из других пакетов: поэтому fmt.Println доступен вам сразу после import "fmt". Если бы она называлась println со строчной, за пределами пакета fmt её бы никто не увидел, и вы ловили бы ошибку «undefined». То же правило работает для типов, структур, констант и переменных уровня пакета: заглавная — наружу, строчная — только внутри пакета. Механизм на одну букву, а покрывает весь вопрос видимости. Мне он кажется честным решением: видно прямо в месте вызова, экспортируется функция или это чья-то внутренняя кухня. Спорить, конечно, можно — в больших структурах не хватает промежуточного уровня «видно внутри модуля», — но для чтения кода это гениально просто.

**Вопрос:** Что произойдёт при попытке собрать программу без func main?

- Программа соберётся, но ничего не выведет
- ✅ Сборка упадёт: в package main нет функции main
- Соберётся библиотека вместо программы
- Компилятор сгенерирует main автоматически

package main без main — ошибка сборки («function main is undeclared in the main package»). Библиотеки живут в других пакетах — там main не нужен и не проверяется.

**Вопрос:** Какой импорт сломает компиляцию?

- ✅ import "strings", когда strings в файле не вызывается
- import "strings", когда strings вызывается один раз
- Два пакета в одной секции import в скобках
- Импорт со знаком подчёркивания: _ "some/package"

Неиспользуемый импорт — ошибка «imported and not used». Импорт с подчёркиванием — легальный способ подключить пакет ради побочных эффектов, он компилятор не злит.

### Соберите скелет программы

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

```go
___ main

___ "fmt"

___ main() {
        fmt.Println("Скелет собран")
}
```

Подсказка: Сверху вниз: объявление пакета, подключение библиотеки, объявление функции.

Правильные ответы в пропусках: `package`, `import`, `func`

### Точки с запятой, комментарии и gofmt

Вы могли заметить: в коде нет точек с запятой. Они в Go есть, но их вставляет лексер автоматически в конце строк — руками ставить не нужно, а лишние gofmt при форматировании убирает. Из этой автоматизации следует неожиданное правило: открывающая фигурная скобка обязана стоять на той же строке, что и объявление. Перенесёте её вниз — лексер поставит точку с запятой не туда, и компилятор выдаст синтаксическую ошибку. Привыкайте сразу, это второй по популярности сюрприз у пришедших из JavaScript. Комментарии — двойной слэш // до конца строки и блочные /* … */ как в C. У комментария над объявлением функции есть особая роль: это официальная документация, по которой работает go doc и сайт pkg.go.dev. Поэтому к таким комментариям пишут полное предложение, начиная с имени функции. Форматирование наконец — gofmt, и это не рекомендация: весь код в экосистеме отформатирован им, споры о пробелах в Go закончились.

> **Имена файлов и папок.** Пакет живёт в папке с тем же именем, файлы имеют суффикс .go. Тестовые файлы заканчиваются на _test.go и в обычную сборку не попадают. Имена файлов принято писать в нижнем регистре без пробелов — myutil.go, а не My Util.go.

Скелет вы теперь читаете свободно: package объявляет принадлежность, import — список зависимостей, func main — где начинается выполнение. Следующий шаг — наполнить main жизнью: в уроке [Переменные и константы](/courses/go-basics/lessons/04-peremennye-konstanty/) начинается разговор о данных, без которых Println так и будет печатать одни приветствия. Когда позже захочется разрезать проект на несколько своих пакетов и подключать чужие библиотеки, вернитесь к продвинутому уроку [Пакеты и модули](/courses/go-advanced/lessons/14-pakety-i-moduli/) — но сперва лучше пройти базу целиком.

## Коротко: что запомнить

- Первая строка файла — package: main для программ, имя папки — для библиотек.
- import перечисляет используемые пакеты; неиспользуемый импорт — ошибка компиляции, а не предупреждение.
- func main — точка входа: обязана быть в package main, без параметров и без возврата.
- Программа живёт до конца main — фоновые задачи умирают вместе с ним.
- Порядок объявления функций в пакете не важен — важен порядок вызова.
- Заглавная буква имени экспортирует его из пакета, строчная оставляет внутренним.
- Точки с запятой вставляет лексер, поэтому открывающая скобка стоит на строке объявления; gofmt решает вопросы стиля за всех.

## Домашнее задание

Возьмите hello world из урока и проведите три эксперимента: удалите строку import и запустите; переименуйте main в main2 и запустите; добавьте свою функцию и вызовите её из main до и после Println. Каждый раз внимательно прочитайте сообщение компилятора — эти три формулировки ошибок вы теперь будете узнавать в лицо весь курс.

## Вопросы и ответы по уроку

### Можно ли объявить две функции main в одном проекте?

Внутри одного пакета main функция main объявляется один раз — иначе ошибка компиляции. Несколько отдельных main-пакетов в разных папках допустимы: так устроены проекты с утилитами в каталогах cmd.

### Почему в Go не ставят точку с запятой?

Она есть, но лексер вставляет её автоматически в конце строк. Из-за этого открывающая фигурная скобка обязана стоять на той же строке, что и объявление, — иначе расстановка собьётся и код не соберётся.

### Чем package main отличается от остальных пакетов?

package main — инструкция собрать исполняемый файл с точкой входа func main. Любое другое имя пакета даёт библиотеку: её функции импортируют другие программы, а запустить её саму нельзя.
