Перейти к содержанию

Лекция 1. Знакомство с Go

Что такое Go и зачем он нужен

Go (или Golang — так часто пишут, чтобы было удобнее гуглить) — язык программирования, который разработали в Google три инженера: Роберт Гризмер, Роб Пайк и Кен Томпсон. Работа началась в 2007 году, публичный анонс был в ноябре 2009-го, а версия 1.0 вышла в марте 2012-го. Один из авторов — Кен Томпсон — это тот самый Томпсон, который вместе с Деннисом Ритчи стоял у истоков Unix и языка B (предка C). Роб Пайк — соавтор UTF-8 и операционной системы Plan 9. То есть Go придумали люди, которые буквально написали половину того, на чём держится современная вычислительная техника.

Мотивация была простой: внутри Google к концу нулевых сборка проекта на C++ занимала десятки минут, иерархия классов разрослась, а конкурентность через потоки операционной системы плохо масштабировалась. Хотелось язык, который:

  • быстро компилируется (секунды, а не минуты);
  • просто читается (минимум синтаксиса);
  • имеет встроенную конкурентность (горутины и каналы);
  • производит один статически слинкованный бинарник (легко деплоить — никакого «у меня на машине работает»);
  • строго типизирован, но с короткими аннотациями.

В итоге Go получился намеренно «маленьким» языком: 25 ключевых слов (для сравнения, в Python около 35, а в C++ — больше 90). Нет наследования классов, нет дженериков долгое время не было (появились только в Go 1.18 в 2022 году), нет исключений в привычном смысле, нет тернарного оператора. Зато есть прямолинейная композиция через структуры, интерфейсы с «утиной» типизацией, и горутины — лёгкие потоки, которых на одной машине можно запустить миллионы.

Сегодня на Go написаны: Docker, Kubernetes, Terraform, Prometheus, Grafana, etcd, CockroachDB, Vault, большая часть инфраструктуры Cloudflare и Dropbox. Если вы будете работать в DevOps или платформенной разработке — Go встретится почти гарантированно.

Установка

macOS

Через Homebrew:

brew install go

Или скачать .pkg с go.dev/dl.

Linux

Самый надёжный способ — скачать архив с официального сайта и распаковать в /usr/local:

wget https://go.dev/dl/go1.23.0.linux-amd64.tar.gz
sudo rm -rf /usr/local/go
sudo tar -C /usr/local -xzf go1.23.0.linux-amd64.tar.gz
echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc
source ~/.bashrc

Пакеты из репозиториев дистрибутивов часто отстают на несколько минорных версий — для учебных целей сойдёт, но для работы лучше ставить вручную или через g (аналог nvm для Go).

Windows

Скачать .msi с go.dev/dl и запустить. После установки PATH прописывается автоматически.

Проверка

go version
# go version go1.23.0 darwin/arm64

GOPATH, модули и где жить коду

Исторически Go требовал, чтобы весь код жил в одной директории $GOPATH/src/<полный-путь-репозитория> — например, ~/go/src/github.com/jtprogru/myproject. Это была боль: невозможно держать проект где удобно, версии зависимостей не фиксировались.

С версии Go 1.11 (2018 г.) появились модули (go mod), а с Go 1.16 они стали поведением по умолчанию. Теперь любой проект может лежать где угодно — главное, чтобы в корне был файл go.mod.

mkdir hello && cd hello
go mod init github.com/jtprogru/hello

После этого появится файл go.mod:

module github.com/jtprogru/hello

go 1.23

Путь github.com/jtprogru/hello — это имя модуля. По нему другие проекты будут импортировать ваш код. Если вы не собираетесь публиковать модуль, имя может быть любым (например, просто hello), но префикс с доменом — общепринятая практика.

Первая программа

Создайте файл main.go:

package main

import "fmt"

func main() {
    fmt.Println("Привет, Go!")
}

Запуск:

go run main.go
# Привет, Go!

Сборка в бинарник:

go build -o hello main.go
./hello
# Привет, Go!

Размер бинарника — около 2 МБ для пустой программы (это вместе с рантаймом и сборщиком мусора). Внутри нет внешних зависимостей: его можно скопировать на голую Linux-машину без установленного Go и запустить.

Что значит каждая строка

  • package main — пакет с именем main означает «исполняемая программа», а не библиотека. Каждый Go-файл обязан начинаться с package <name>.
  • import "fmt" — подключаем пакет fmt из стандартной библиотеки (форматированный ввод-вывод).
  • func main() — точка входа. Принимает 0 аргументов, ничего не возвращает.
  • fmt.Println(...) — выводит аргументы через пробел и добавляет \n в конце.

Структура проекта

Простой проект:

hello/
├── go.mod
├── go.sum            # появится после добавления зависимостей
├── main.go
└── README.md

Большой проект с несколькими пакетами:

myapp/
├── go.mod
├── go.sum
├── cmd/
│   └── server/
│       └── main.go           # точка входа (package main)
├── internal/                 # пакеты, которые нельзя импортировать извне модуля
│   ├── handler/
│   │   └── handler.go
│   └── storage/
│       └── postgres.go
├── pkg/                      # пакеты, доступные внешним проектам (опционально)
│   └── client/
│       └── client.go
└── README.md

Стандарта де-юре для структуры проекта в Go нет, но соглашения уровня golang-standards/project-layout фактически прижились в индустрии. Особенно важна директория internal/ — это синтаксическая фича компилятора: пакеты внутри internal/ могут импортировать только модули того же дерева, что и сам internal/. Удобно, чтобы внутренняя реализация не утекала наружу.

Команды Go-инструмента

Утилита go — это «швейцарский нож»: компилятор, форматировщик, тест-раннер, менеджер зависимостей и линтер в одной обёртке.

Команда Что делает
go run file.go Скомпилировать и сразу запустить (бинарник во временной директории, удаляется после выполнения).
go build Скомпилировать в бинарник в текущей директории. По умолчанию имя бинарника = имени директории.
go install Скомпилировать и положить бинарник в $GOPATH/bin (обычно ~/go/bin).
go test ./... Запустить все тесты во всех пакетах модуля (./... — рекурсивный wildcard).
go fmt ./... Отформатировать код по канону. Запускайте перед каждым коммитом (или настройте on-save в IDE).
go vet ./... Статический анализ: подозрительные конструкции (например, неиспользуемый результат).
go mod tidy Привести go.mod и go.sum в порядок: удалить неиспользуемые зависимости, добавить недостающие.
go get <pkg> Добавить или обновить зависимость.
go doc <pkg> Документация по пакету в терминале (например, go doc fmt.Println).

Форматирование: gofmt и goimports

В Go нет холиваров про tabs vs spaces, нет двух школ расстановки скобок. Есть один официальный форматтер — gofmt — и одно правильное оформление. Запуск:

gofmt -w .
# или
go fmt ./...

gofmt использует табы для отступов и переформатирует код по строгим правилам. На практике обычно используют чуть более продвинутую утилиту goimports — она делает то же самое, плюс автоматически добавляет/удаляет импорты:

go install golang.org/x/tools/cmd/goimports@latest
goimports -w .

Любая IDE с поддержкой Go умеет запускать goimports на сохранение — настройте это сразу.

Линтер: golangci-lint

go vet ловит только очевидные ошибки. Для серьёзной статической проверки используют golangci-lint — мета-линтер, который запускает десятки отдельных линтеров (errcheck, gosec, staticcheck, revive, ineffassign и др.):

brew install golangci-lint
# или
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest

golangci-lint run ./...

Конфигурируется через .golangci.yml в корне проекта. В индустрии это де-факто стандарт — добавляйте сразу в CI.

VS Code для Go

В лекции 1 темы 7 уже описывали установку VS Code. Для Go нужны:

  1. Официальное расширение Go от команды Google (golang.go).
  2. После установки расширения VS Code предложит «Install/Update Tools» — соглашайтесь, оно поставит gopls (language server), dlv (отладчик), goimports, staticcheck и др.
  3. Включите в settings.json:
{
  "go.useLanguageServer": true,
  "go.formatTool": "goimports",
  "go.lintTool": "golangci-lint",
  "go.lintOnSave": "package",
  "[go]": {
    "editor.formatOnSave": true,
    "editor.codeActionsOnSave": {
      "source.organizeImports": "explicit"
    }
  }
}

После этого редактор сам форматирует код, расставляет импорты, подсвечивает ошибки и предлагает рефакторинги.

Параллель с Python

Python Go
python script.py go run main.go
pip install requests go get github.com/some/package
pyproject.toml + uv.lock go.mod + go.sum
виртуальное окружение модуль (go mod init) — изоляции на уровне FS нет, версии зависимостей в go.mod
if __name__ == "__main__": func main() в package main
интерпретатор + байт-код компиляция в нативный бинарник
black, ruff format gofmt, goimports
ruff check, mypy go vet, golangci-lint
pytest go test ./...

Что почитать

  • The Go Programming Language Specification — спецификация языка, читается за вечер;
  • A Tour of Go — интерактивный туториал на 1–2 часа;
  • Effective Go — каноническое описание идиом;
  • Go by Example — короткие примеры на все основные темы;
  • книга Алана Донована и Брайана Кернигана «The Go Programming Language» (есть русский перевод).

Итог

Go — намеренно простой, быстро компилируемый, статически типизированный язык с встроенной конкурентностью. Один бинарник, один форматтер, один официальный способ управления зависимостями (go mod). В следующей лекции — переменные, типы и управляющие конструкции.