8. go fmt и goimports. Форматирование кода

Один из самых больших культурных шоков для разработчиков, переходящих с C#, Java или C++, — это полное и абсолютное отсутствие споров о стиле кода. В большинстве других экосистем десятилетиями полыхают бесконечные "священные войны": ставить ли открывающую фигурную скобку на новой строке (Allman) или на той же (K&R), использовать 2 пробела, 4 пробела или табуляцию, ставить ли пробел после ключевого слова if.

В Go этот вопрос закрыт раз и навсегда. Есть gofmt, и есть канонический стиль. Всё остальное — синтаксическая ошибка или повод для немедленного автоформатирования.


Философия: Gofmt's style is not my style, it's the style

Роб Пайк однажды сформулировал это с характерным прагматизмом: "Gofmt's style is no one's favorite, yet gofmt is everyone's favorite". Стиль gofmt не является чьим-то идеалом, но сам факт его повсеместного и обязательного существования радует всех без исключения.

go fmt (и его нижележащий инструмент gofmt) форматирует исходный код по единому, жестко зафиксированному стандарту. Это часть концепции Mechanical Sympathy, но применительно не к процессору, а к человеческому восприятию: код должен выглядеть одинаково в любом проекте мира, чтобы мозг инженера не тратил драгоценные когнитивные ресурсы на парсинг чужих эстетических предпочтений, а сразу фокусировался на архитектуре и логике.


Как это работает (Under the hood)

gofmt не работает с текстом как с набором сырых строк или регулярных выражений. Он читает исходный код, парсит его в полное Абстрактное Синтаксическое Дерево (AST) через пакет go/parser, а затем сериализует его обратно в текстовый вид через движок печати go/printer и text/tabwriter, руководствуясь строгими грамматическими правилами.

Это означает, что gofmt не просто подправляет пробелы. Он структурно перестраивает код, выравнивая поля структур, констант и переменных по колонкам с помощью символов табуляции:

Go
// До gofmt (хаос)
struct {
    Name string
    Age int
    IsActive bool
}

// После gofmt (автоматическое выравнивание)
struct {
    Name     string
    Age      int
    IsActive bool
}

go fmt vs gofmt

Разработчики часто путают эти две утилиты:

  • gofmt — это низкоуровневый системный инструмент форматирования. Он принимает файлы или каталоги и по умолчанию выводит результат форматирования в stdout (для перезаписи исходного файла на диске требуется флаг -w).
  • go fmt — это высокоуровневая команда-драйвер Go toolchain. Она оперирует путями пакетов Go (./...) и под капотом вызывает gofmt -l -w для всех обнаруженных файлов пакета.

Команды повседневного использования:

  • gofmt -w main.go — отформатировать и перезаписать конкретный файл.
  • go fmt ./... — рекурсивно отформатировать весь проект.

В CI/CD скриптах часто используют проверку без модификации файлов:

Bash
# Проверка, отформатирован ли код (для CI)
gofmt -l .
# Если вывод пуст, всё ок. Если есть список файлов, значит они не отформатированы.

goimports: Следующий уровень

Хотя go fmt входит в стандартный дистрибутив, в реальной разработке его почти полностью вытеснил инструмент goimports (разрабатываемый командой Go в репозитории golang.org/x/tools/cmd/goimports).

goimports делает абсолютно всё то же самое, что и gofmt, но решает еще одну важнейшую задачу: автоматически управляет блоком import.

  1. Удаление неиспользуемых импортов: Если вы удалили или закомментировали вызов fmt.Println, goimports молча удалит строку import "fmt", предотвращая ошибку компиляции "imported and not used".
  2. Добавление недостающих импортов: Если вы написали вызов json.Marshal, goimports автоматически найдет пакет и добавит строку import "encoding/json".

Логика группировки импортов

goimports организует импорты в строгом порядке, разделяя их пустыми строками на три логические группы:

  1. Standard library: fmt, os, context.
  2. Third-party: github.com/gin-gonic/gin, go.uber.org/zap.
  3. Local packages: myproject/internal/service (настраивается флагом -local).
Go
package main

import (
    "fmt" // 1. Стандартная библиотека
    "os"

    "github.com/gin-gonic/gin" // 2. Сторонние
    "go.uber.org/zap"

    "myproject/internal/handler" // 3. Локальные
)

// ...
flowchart TD
    classDef process  fill:#1e293b,stroke:#38bdf8,stroke-width:1px,color:#f8fafc
    classDef success  fill:#064e3b,stroke:#34d399,stroke-width:1px,color:#f8fafc
    classDef error    fill:#7f1d1d,stroke:#f87171,stroke-width:1px,color:#f8fafc
    classDef warning  fill:#78350f,stroke:#fbbf24,stroke-width:1px,color:#f8fafc
    classDef entry    fill:#1e3a5f,stroke:#38bdf8,stroke-width:2px,color:#f8fafc
    classDef aux      fill:#334155,stroke:#64748b,stroke-width:1px,color:#f8fafc
    classDef system   fill:#312e81,stroke:#a78bfa,stroke-width:1px,color:#f8fafc
    classDef data     fill:#132d29,stroke:#2dd4bf,stroke-width:1px,color:#f8fafc
    classDef network  fill:#881337,stroke:#fb7185,stroke-width:1px,color:#f8fafc
    classDef accent   fill:#1e1b4b,stroke:#818cf8,stroke-width:1px,color:#f8fafc
    A["Сохранение файла"]:::process --> B["goimports"]
    B --> C["Парсинг AST"]
    C --> D["Анализ использования"]
    D --> E{"Импорт использован?"}
    E -- Нет --> F["Удалить import"]
    E -- Да --> G["Оставить или добавить import"]
    F --> H["Сортировка по группам"]
    G --> H
    H --> I["Форматирование gofmt"]
    I --> J["Запись файла"]
  

Интеграция с IDE и CI

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

  1. IDE: Настройте "Format on Save" в VSCode или GoLand, указав goimports в качестве дефолтного форматтера. Это мгновенно выравнивает код и правит импорты при каждом нажатии Ctrl+S / Cmd+S.
  2. CI: Добавьте обязательный шаг в пайплайн, проверяющий gofmt -l. Это не позволит неаккуратному разработчику залить неформатированный код в общий репозиторий.
Bash
# Пример проверки в Makefile
lint:
    @if [ -n "$$(gofmt -l .)" ]; then \
        echo "Code is not formatted. Run 'gofmt -w .'"; \
        exit 1; \
    fi

Итог

  1. gofmt — стандарт де-факто. Споры о стиле бессмысленны.
  2. Работает через AST, гарантируя валидность кода и умное выравнивание полей структур.
  3. goimports — must-have инструмент, объединяющий форматирование и управление зависимостями.
  4. Интегрируйте эти инструменты в IDE и CI для автоматического применения.

Код отформатирован, импорты расставлены. Но что если нам нужно сгенерировать код до того, как его увидит компилятор? В следующей статье мы разберем мощный механизм кодогенерации: 9. go generate. Генерация кода.