Форматирование кода Go: пакет `format` и команда `go fmt`

Форматирование кода — важная практика для повышения читаемости, консистентности и повторного использования. В экосистеме Go есть чёткие правила форматирования, и инструментальная поддержка позволяет автоматически привести код к этим правилам.
О чём эта статья
- Что такое пакет format и команда go fmt.
- Как использовать go fmt в командной строке и из CI.
- Как форматировать исходники из программы с помощью go/format и Config.
- Когда автоматическое форматирование не подходит и альтернативы.
- Практические чеклисты для разработчика и ревьюера, краткий справочник и критерии приёмки.
Пакет format и команда go fmt
Пакет go/format реализует стандартное форматирование исходного кода Go. Команда go fmt (alias для gofmt) применяет те же правила форматирования из командной строки и обычно используется в локальной разработке и CI.
Чтобы просмотреть помощь по команде fmt, выполните:
go help fmtЧтобы форматировать файл вручную:
go fmt main.goВ действительности go fmt вызывает gofmt с флагами, эквивалентными следующей команде:
gofmt -l -w- -l — выводит имена изменённых файлов.
- -w — перезаписывает файлы с применённым форматированием.
Флаг -x показывает команды, которые будут выполнены:
go fmt -x main.goФлаг -n показывает, какие команды были бы выполнены, не внося изменений:
go fmt -n main.goПростой пример программы
Ниже — упрощённый пример программы, которую можно отформатировать с помощью go fmt.
package main
import (
"fmt"
)
func main() {
var x int = 5
for i := 0; i < x; i++ {
fmt.Println("Hello World!")
}
}Форматирование исходников из программы: format.Source
Если нужно форматировать код программно (например, в редакторе, генераторе кода или инструменте), используйте функцию format.Source. Она принимает срез байт с исходным кодом и возвращает отформатированный срез байт.
Пример чтения файла, форматирования и записи обратно:
package main
import (
"io/ioutil"
"log"
"go/format"
)
func main() {
fileContent, err := ioutil.ReadFile("main.go")
if err != nil {
log.Fatalln("Ошибка чтения файла:", err)
}
formatted, err := format.Source(fileContent)
if err != nil {
log.Fatalln("Ошибка форматирования:", err)
}
if err := ioutil.WriteFile("main.go", formatted, 0644); err != nil {
log.Fatalln("Ошибка записи файла:", err)
}
}Важно: в современных версиях Go рекомендуют использовать os.ReadFile/os.WriteFile вместо ioutil (ioutil частично устарел), но приведённый код остаётся рабочим и часто встречается в существующих проектах.
Форматирование кода, переданного в память
Можно отформатировать строку или срез байт с кодом без работы с файлами:
package main
import (
"fmt"
"go/format"
"log"
)
func main() {
src := `package main
import (
"fmt"
"math"
)
func main() {
var a float64 = 3
var b float64 = 4
var c float64 = 5
var s float64 = (a + b + c) / 2
var area float64 = math.Sqrt(s * (s-a) * (s-b) * (s-c))
fmt.Println("The area of the triangle is:", area)
}
`
formatted, err := format.Source([]byte(src))
if err != nil {
log.Fatalln("Ошибка форматирования:", err)
}
fmt.Println(string(formatted))
}После форматирования обычно нужно преобразовать срез байт в строку: string(formatted).
Настройка форматирования с помощью Config
Пакет go/format предоставляет тип Config, который позволяет настроить поведение форматтера перед применением к данным. Пример создания конфигурации:
import "go/format"
config := &format.Config{
Tabwidth: 8,
UseTabs: false,
TabIndent: true,
NoFinalTab: true,
Spaces: true,
NoTrimTrailingSpace: false,
}Когда вы используете config.Source, это применит выбранные опции к переданному срезу байт:
formatted, err := config.Source(fileContent)
if err != nil {
log.Fatalln("Ошибка форматирования с Config:", err)
}Параметры позволяют контролировать ширину таба, замену табов пробелами, удаление финальных табуляций и т.д. Используйте эти параметры, если у вас есть специфические требования к форматированию в рамках генераторов кода или внутренних инструментов.
Что форматирование делает и чего не делает
Важно понимать границы автоматического форматирования:
- Форматирование касается пробелов, табуляций, пробелов вокруг операторов, отступов и выравнивания. Оно не изменяет логику программы.
- Форматирование не исправляет синтаксические ошибки. Если исходный код содержит ошибки парсинга, format.Source вернёт ошибку.
- Форматирование не заменяет линтер: go fmt не выявляет потенциальные баги, не даёт советов по стилю именования и архитектуре.
Когда форматирование не подходит (контрпример)
- Генераторы кода, которые намеренно выравнивают колонки в табличном виде и полагаются на специфическое «вручную» выровненное представление; после go fmt такое выравнивание будет утрачено.
- Фрагменты кода в документации (README), где формат важен для визуального восприятия, но который не должен быть изменён автоматически.
- Если вы используете нестандартный стиль (очень редкий случай), автоматическое форматирование может противоречить вашим правилам.
Альтернативные подходы
- Использовать gofmt с разными флагами напрямую (gofmt имеет дополнительные опции).
- Для проверки стиля в CI: запускать go fmt -l и падать при наличии изменённых файлов.
- Применять go vet, staticcheck и linters для выявления проблем, которые go fmt не покрывает.
Модель принятия решения (хитрости и эвристики)
- Всегда применять go fmt при сохранении файла в редакторе (IDE-плагины делают это автоматически).
- На этапе коммита: добавьте хуки pre-commit, которые запускают go fmt и отменяют коммит при изменениях.
- В CI: запускайте go fmt -l; если есть непокрытые файлы — фейлить сборку.
flowchart TD
A[Изменён файл .go] --> B{Форматирование нужно?}
B -- Да --> C[Запустить go fmt]
C --> D{gofmt вернул изменения?}
D -- Да --> E[Ошибка в CI / отмена коммита]
D -- Нет --> F[Продолжить]
B -- Нет --> FЧеклисты по ролям
Разработчик:
- Запустил go fmt локально или настроил автоформат при сохранении.
- Прогнал тесты после форматирования.
- Проверил, что git diff содержит только форматирование, а не непреднамерённые изменения логики.
Ревьюер:
- Игнорировать изменения, которые касаются только форматирования (если они отдельным коммитом).
- Потребовать отвязки функциональных изменений от форматирования (разные коммиты).
- В CI требовать, чтобы все файлы были отформатированы автоматически.
Инженер DevOps:
- Добавить шаг в pipeline: go fmt -l ./… и падать при ненулевом выводе.
- Обеспечить, чтобы посткоммитные хуки на CI не меняли содержимое репозитория незаметно для разработчика без уведомления.
Шпаргалка команд
- Просмотр помощи: go help fmt
- Локальное форматирование файла: go fmt main.go
- Форматирование всех пакетов: go fmt ./…
- Показать, какие файлы изменятся: gofmt -l -w
- Имитировать изменения: go fmt -n
- Показать команды: go fmt -x
Критерии приёмки
- Все изменённые файлы проходят проверку go fmt в CI (go fmt -l ./… не выводит файлов).
- Коммиты разделяют форматирование и функциональные изменения.
- Комментарии и документация не содержат неотформатированные блоки кода, если они исполняемые — обеспечить отдельную проверку.
Короткий справочник терминов
- go fmt /gofmt — инструмент форматирования Go-кода.
- format.Source — функция пакета go/format для форматирования исходного кода в памяти.
- Config — структура в go/format для настройки поведения форматтера.
Советы по внедрению в команду
- Настройте редакторы (VS Code, GoLand и т. п.) на автоформат при сохранении.
- Внедрите pre-commit hook (например, с использованием pre-commit или husky), который запускает go fmt.
- В CI включите проверку: go fmt -l ./… и блокируйте PR, если список файлов не пуст.
Риски и способы их снижения
- Риск: массовое форматирование затруднит ревью большого PR.
- Смягчение: разбивайте коммиты — первый коммит только с форматированием, последующие с логикой.
- Риск: форматирование нарушит выровненные таблицы в сгенерированных файлах.
- Смягчение: добавьте правила для генераторов кода, чтобы они форматировали выходной код после генерации.
Итог
Автоматическое форматирование — быстрый выигрыш для качества кода и скорости ревью. Внедрите go fmt в рабочий процесс: локально в редакторе, как pre-commit hook и как проверку в CI. Используйте пакет go/format и Config для форматирования из кода, когда нужны кастомные инструменты или генераторы.
Важно: форматирование не заменяет статический анализ и код-ревью — это инструмент для согласованности представления кода, а не для поиска логических ошибок.
Похожие материалы
RDP: полный гид по настройке и безопасности
Android как клавиатура и трекпад для Windows
Советы и приёмы для работы с PDF
Calibration в Lightroom Classic: как и когда использовать
Отключить Siri Suggestions на iPhone