Первая программа на Go
Первая программа на Go
Go — это компилируемый язык, и для работы с ним требуется установка toolchain — набора утилит командной строки, включающего в себя компилятор go, менеджер зависимостей, средство сборки, тестирования и анализа кода. Этот инструментарий распространяется как единый пакет, доступный для всех основных операционных систем: Windows, macOS и Linux.
Для написания кода не требуется установка тяжёлой интегрированной среды разработки (IDE) вроде IntelliJ IDEA или Visual Studio. Хотя такие среды поддерживают Go через плагины (GoLand, VS Code с расширением Go и др.), на начальном этапе достаточно текстового редактора с базовой поддержкой синтаксиса и возможностью вызова команд из терминала. Наиболее популярным и рекомендуемым выбором является Visual Studio Code — лёгкий, кроссплатформенный редактор с открытым исходным кодом, разрабатываемый Microsoft. Его преимущества включают:
— встроенную интеграцию с терминалом,
— поддержку автодополнения, навигации по коду и рефакторинга через официальное расширение Go от команды Go,
— прозрачную работу с модулями, тестами и профилированием,
— отсутствие необходимости в настройке сложной инфраструктуры для старта.
Альтернативные варианты включают:
— Vim или Neovim с плагинами vim-go и coc.nvim, если предпочтителен клавиатурно-ориентированный подход и минимализм,
— Sublime Text с пакетом GoSublime,
— GoLand от JetBrains — полноценная платная IDE, оптимальная при интенсивной разработке, но избыточная для первых шагов.
Выбор редактора не влияет на сам процесс компиляции и запуска, так как сборка проекта производится исключительно через команды go build и go run, независимо от среды написания. Поэтому дальнейшее описание будет ориентировано на универсальный сценарий: использование Visual Studio Code в сочетании с системным терминалом (или встроенным терминалом редактора).
Установка Go
Первым действием является загрузка официального дистрибутива с сайта golang.org/dl. На странице представлены установочные пакеты для всех поддерживаемых платформ — .msi для Windows, .pkg для macOS, .tar.gz для Linux. Рекомендуется выбирать последнюю стабильную версию, обозначаемую как Stable. На момент написания главы это Go 1.23.x, но версия может измениться — ориентироваться следует на дату релиза и пометку Stable, а не на номер.
Для Linux и macOS рекомендуется использовать архивный вариант (goX.XX.X.linux-amd64.tar.gz или аналогичный). Установка производится вручную: архив распаковывается в системную директорию, например /usr/local, после чего исполняемые файлы становятся доступны глобально. Типичная последовательность команд в терминале:
# Скачивание архива (пример для amd64-системы)
wget https://go.dev/dl/go1.23.1.linux-amd64.tar.gz
# Удаление предыдущей установки Go, если она есть в /usr/local/go
sudo rm -rf /usr/local/go
# Распаковка архива в /usr/local
sudo tar -C /usr/local -xzf go1.23.1.linux-amd64.tar.gz
# Добавление /usr/local/go/bin в PATH (в ~/.bashrc или ~/.zshrc)
echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc
source ~/.bashrc
После этого команда go version в новом сеансе терминала должна вывести строку вида go version go1.23.1 linux/amd64, что подтверждает успешную установку.
Для Windows процесс проще: запускается .msi-инсталлятор, который автоматически добавляет путь к go.exe в системную переменную PATH, создаёт стандартные каталоги и настраивает окружение. Проверка также выполняется через go version в командной строке или PowerShell.
Важно отметить: Go не требует установки отдельной Java-машины, .NET Runtime или иных сред выполнения — весь необходимый инструментарий поставляется в комплекте. После установки доступны ключевые команды:
— go build — компиляция исходного кода в исполняемый файл,
— go run — компиляция и немедленный запуск без сохранения бинарного файла,
— go mod init — инициализация нового модуля,
— go doc — просмотр документации,
— go test — запуск тестов,
— go vet, go fmt, go lint — статический анализ и форматирование.
Все эти команды работают из любого каталога файловой системы — глобальное размещение инструментов обеспечивает единообразие среды независимо от места хранения проектов.
Структура проекта и организация исходного кода
В отличие от некоторых языков, где проекты жёстко привязаны к определённой иерархии каталогов (например, Maven-структура в Java), Go с версии 1.11 перешёл на модель модулей (go modules), которая полностью освободила разработчика от необходимости размещать код внутри $GOPATH. Теперь каждый проект — это автономная директория с файлом go.mod, описывающим его идентификатор, версию и зависимости.
Для первой программы не требуется сложная иерархия. Достаточно создать отдельную папку, например hello-go, инициализировать в ней модуль, затем разместить один исходный файл с расширением .go. Поясним каждый шаг.
Шаг 1: Создание рабочей директории
Выберите локацию на диске — например, ~/projects/hello-go (для Linux/macOS) или C:\Users\<имя>\projects\hello-go (для Windows). Создайте каталог любым удобным способом: через командную строку (mkdir -p ~/projects/hello-go), проводник или встроенные средства редактора.
Шаг 2: Инициализация модуля
Перейдите в созданную директорию и выполните:
cd ~/projects/hello-go
go mod init hello-go
Команда создаёт файл go.mod следующего содержания:
module hello-go
go 1.23
Здесь hello-go — это модульный путь (module path), уникальный идентификатор проекта. На данном этапе он может быть произвольным, но по соглашению он часто совпадает с относительным путём или формируется как доменное имя в обратном порядке (например, example.com/hello). Для локальных экспериментов допустимо использовать простое имя без точки, хотя в продакшене рекомендуется применять доменные имена для избежания коллизий.
Файл go.mod — центральный элемент управления зависимостями. При добавлении внешних библиотек (например, через go get github.com/some/library) в него автоматически добавляются записи require, фиксирующие точные версии. Для первой программы зависимости отсутствуют, и файл остаётся минимальным.
Шаг 3: Создание исходного файла
В той же директории создайте файл с именем main.go. Соглашения Go предписывают:
— файлы с точкой входа в программу должны содержать функцию main,
— такая функция должна находиться в пакете main,
— любой файл, принадлежащий пакету main, должен объявлять это в первой исполняемой строке: package main.
Имя файла может быть любым, но традиционно используется main.go для главного модуля. Внутри файла размещается исходный код — последовательность инструкций на языке Go.
Написание кода
Go следует строгой дисциплине оформления кода. Каждая программа начинается с объявления пакета. Пакет — это логическая единица кодовой базы, объединяющая один или несколько файлов. Пакет main имеет особый статус: он обозначает исполняемое приложение. Библиотеки, напротив, используют именованные пакеты, такие как http, fmt, os, и не содержат функции main.
После объявления пакета следует секция импорта — перечисление сторонних пакетов, функциональность которых требуется в текущем файле. Для вывода текста в консоль используется стандартный пакет fmt (от format), предоставляющий функции форматированного ввода-вывода, включая Println, Printf, Scanf и другие.
Тело программы состоит из функций. Единственная обязательная функция в исполняемом приложении — main. Она не принимает аргументов и не возвращает значений. Это точка входа — с неё начинается выполнение программы после загрузки и инициализации всех пакетов.
Типичный минимальный код выглядит так:
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
Разберём каждую строку.
package main — объявление принадлежности файла к главному исполняемому пакету. Эта строка обязательна и должна быть первой (после возможных комментариев).
import "fmt" — директива импорта. Она указывает, что в коде будут использоваться экспортируемые идентификаторы из пакета fmt. Имя пакета при импорте совпадает с именем директории внутри $GOROOT/src (для стандартной библиотеки) или репозитория (для внешних). Экспортируемые идентификаторы — это те, чьи имена начинаются с заглавной буквы: Println, Error, Reader и т.д. Внутренние (непубличные) идентификаторы, начинающиеся со строчной буквы (println, errorf), недоступны вне пакета.
func main() { ... } — определение функции. Ключевое слово func вводит сигнатуру функции. Скобки () обозначают отсутствие параметров. Фигурные скобки {} ограничивают тело функции — блок кода, выполняемый при вызове.
fmt.Println("Hello, World!") — вызов функции Println из пакета fmt. Эта функция принимает произвольное число аргументов любого типа, преобразует их в строковое представление, объединяет пробелами и выводит в стандартный поток вывода stdout, завершая строку символом перевода строки \n. Аргумент "Hello, World!" — строковый литерал в двойных кавычках. В Go строки неизменяемы и кодируются в UTF-8 по умолчанию.
Обратите внимание на отсутствие точки с запятой. Go автоматически вставляет их в конце строк при определённых условиях (например, перед закрывающей скобкой или новой инструкцией), поэтому вручную их ставить не требуется. Это упрощает чтение и снижает количество шумовых символов.
Также важна идентация: тело функции сдвинуто на одну табуляцию (или 4 пробела — настройка редактора). Go не требует строгого соответствия стилю, но инструмент go fmt автоматически приводит код к каноническому виду, включая выравнивание, пробелы вокруг операторов и порядок импортов. Регулярное применение go fmt рекомендуется даже на начальном этапе — это формирует дисциплину и совместимость с экосистемой.
Запуск программы
После сохранения файла main.go программа готова к запуску. Go предоставляет два основных способа: интерпретируемый (на самом деле — компиляция в памяти с немедленным запуском) и компиляция в отдельный исполняемый файл.
Способ 1: go run
Самый быстрый путь — команда go run, за которой следует имя файла (или маска, например *.go):
go run main.go
Результат выполнения:
Hello, World!
Под капотом go run выполняет следующие действия:
— анализирует зависимости модуля через go.mod,
— при необходимости загружает недостающие пакеты (в данном случае — только стандартная библиотека, уже присутствующая в дистрибутиве),
— компилирует исходный код в машинный код целевой архитектуры,
— создаёт временный исполняемый файл в системном каталоге (например, /tmp/go-build...),
— запускает его, передавая аргументы командной строки (если указаны),
— после завершения удаляет временный файл.
Этот способ оптимален для разработки: изменения в коде сразу отражаются при следующем запуске, не требуя ручной пересборки. Минус — каждый запуск включает фазу компиляции, что может быть заметно при больших проектах (хотя Go славится высокой скоростью сборки даже для миллионов строк).
Способ 2: go build и запуск исполняемого файла
Для получения постоянного артефакта используется go build:
go build -o hello
Флаг -o задаёт имя выходного файла. Без него имя совпадает с именем директории (hello-go на Unix-системах или hello-go.exe на Windows). Результат — нативный бинарный файл, не требующий интерпретатора или дополнительных библиотек. Его можно запустить напрямую:
./hello # Linux/macOS
hello.exe # Windows (в PowerShell или cmd)
Преимущества этого подхода:
— отсутствие накладных расходов на компиляцию при каждом запуске,
— возможность передачи файла другому пользователю (на той же ОС и архитектуре) без установки Go,
— интеграция в CI/CD-конвейеры, где сборка и запуск разделены во времени.
Go поддерживает кросс-компиляцию без дополнительных инструментов: достаточно задать переменные окружения GOOS и GOARCH перед сборкой. Например, сборка Windows-версии на Linux:
GOOS=windows GOARCH=amd64 go build -o hello.exe
Полученный hello.exe будет работать на любой 64-битной Windows-машине без установки зависимостей.
Анализ окружения Go
После установки go предоставляет команду go env, выводящую полный набор переменных окружения, используемых при сборке, тестировании и компиляции. Знание этих параметров необходимо для диагностики проблем и понимания поведения инструментария.
Ключевые переменные:
-
GOROOT— путь к корневой директории установленного дистрибутива Go (например,/usr/local/go). Внутри находятся подкаталогиbin(исполняемые утилиты),src(исходный код стандартной библиотеки),pkg(скомпилированные пакеты). Эта переменная устанавливается автоматически при корректной инсталляции и редко требует ручной настройки. -
GOPATH— исторически это был обязательный путь к рабочему пространству, содержащемуsrc,bin,pkg. С введением модулей (Go 1.11+)GOPATHстал опциональным: проекты могут размещаться в любом месте файловой системы. Однако каталогGOPATH/binпо-прежнему используется как место установки глобальных утилит (например,golangci-lint,mockgen), если они устанавливаются черезgo install. По умолчаниюGOPATHравен~/goв домашней директории пользователя. -
GOOSиGOARCH— целевая операционная система и архитектура процессора. По умолчанию совпадают с хост-системой, но могут быть переопределены для кросс-компиляции. Поддерживаемые значения документированы в официальной спецификации:GOOSвключаетlinux,darwin(macOS),windows,freebsdи другие;GOARCH—amd64,arm64,386,ppc64le,s390x. -
GO111MODULE— устаревшая переменная, управлявшая режимом модулей. В Go 1.16+ модули включены по умолчанию во всех контекстах, и эта настройка игнорируется. Упоминание необходимо только при работе с устаревшими версиями. -
GOMOD— путь к файлуgo.modтекущего модуля. Если команда запущена вне модуля, значение —/dev/null(Unix) илиnul(Windows).
Выполнение go env без аргументов выводит все переменные; go env GOROOT — только указанную. Это стандартный способ верификации окружения перед началом работы, особенно при одновременном использовании нескольких версий Go (например, через gvm или asdf).
Принципы именования и организация кода в многофайловом проекте
Go придерживается строгой дисциплины именования на всех уровнях. Это часть семантики языка.
-
Пакеты именуются строчными буквами, без подчёркиваний, цифр или дефисов. Короткие, ёмкие имена предпочтительны:
http,json,bytes,errors. Имя пакета не обязано совпадать с именем каталога, но рекомендуется для избежания путаницы. Пакет импортируется по пути, указанному вimport, но в коде обращение идёт по имени пакета, объявленному в первом файле каталога черезpackage <имя>. Таким образом,import "net/http"даёт доступ к функциям черезhttp.Get, а неnet_http.Get. -
Функции и типы в стандартной библиотеке и публичных API следуют принципу: экспортируемые идентификаторы — с заглавной буквы (
Reader,Write,NewRequest), внутренние — со строчной (read,buf,err). Экспорт определяется не модификаторами доступа (их в Go нет), а регистром первой буквы. Это упрощает анализ видимости: достаточно взглянуть на имя. -
Структура проекта для небольшой программы может быть плоской: один файл
main.go, одинgo.mod. По мере роста сложности код разбивается на логические единицы:cmd/— каталог для точек входа. Если проект включает несколько исполняемых файлов (например,serverиcli), каждый получает отдельную поддиректорию:cmd/server/main.go,cmd/cli/main.go.internal/— приватные пакеты, доступные только в рамках текущего модуля. Импорт извне запрещён механизмом сборки.pkg/— публичные утилиты, предназначенные для переиспользования (менее распространено в современных проектах, где предпочитают выносить такие компоненты в отдельные модули).go.modиgo.sumнаходятся в корне.
Для первой программы такая структура избыточна, но понимание её полезно для перехода к более сложным задачам.
Расширение программы
Модифицируем исходный код, чтобы он выводил фиксированную строку и принимал входные данные, реагировал на ошибки — ключевые аспекты любой реальной программы.
Go — язык со статической типизацией и выводом типов. Переменная может быть объявлена явно:
var name string = "World"
или сокращённо, с автоматическим выводом типа по правой части:
name := "World"
Оператор := — это короткое объявление, сочетающее резервирование памяти, присваивание и вывод типа. Он допустим только внутри функций и требует хотя бы одного нового идентификатора слева. Повторное присваивание существующей переменной делается через =.
Тип string в Go — это неизменяемая последовательность байтов в кодировке UTF-8. Длина строки определяется функцией len(), количество Unicode-символов (рун) — utf8.RuneCountInString().
Добавим ввод имени пользователя:
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
fmt.Print("Введите ваше имя: ")
// Создаём буферизованный читатель для os.Stdin
reader := bufio.NewReader(os.Stdin)
// Читаем строку до символа новой строки
name, err := reader.ReadString('\n')
// Проверяем наличие ошибки
if err != nil {
fmt.Fprintf(os.Stderr, "Ошибка ввода: %v\n", err)
os.Exit(1)
}
// Удаляем завершающий символ перевода строки
name = name[:len(name)-1]
// Формируем и выводим приветствие
greeting := "Hello, " + name + "!"
fmt.Println(greeting)
}
Разберём нововведения.
import с группировкой в скобках — стандартный способ подключения нескольких пакетов. Порядок не важен, но go fmt сортирует их лексикографически.
bufio.NewReader(os.Stdin) — создание буферизованного читателя поверх стандартного потока ввода. Буферизация повышает эффективность при многократных операциях чтения.
reader.ReadString('\n') возвращает два значения: прочитанную строку и ошибку. Это идиоматичный для Go подход — множественные возвращаемые значения, где последний элемент почти всегда имеет тип error. Такая сигнатура заставляет разработчика явно обрабатывать исключительные ситуации, не полагаясь на исключения (в Go их нет).
if err != nil — проверка на наличие ошибки. Тип error — это встроенный интерфейс с одним методом Error() string. Любая ненулевая ошибка означает сбой в операции: например, конец файла при интерактивном вводе (редко), или прерывание потока.
os.Exit(1) завершает программу с кодом возврата 1, сигнализируя об ошибке. Код 0 — успешное завершение (по умолчанию при выходе из main).
name[:len(name)-1] — срез строки без последнего символа (\n). Это простейший способ очистки, хотя в реальных сценариях предпочтительнее strings.TrimSpace(name), удаляющий все управляющие символы по краям.
Обратите внимание: отсутствие try/catch, finally, throw. Обработка ошибок — линейная, декларативная, без скрытых ветвлений выполнения. Это делает поток управления предсказуемым и упрощает статический анализ.
Тестирование и верификация
Go включает фреймворк для написания и запуска тестов прямо в стандартной поставке. Тесты размещаются в файлах с суффиксом _test.go и содержат функции, начинающиеся с Test, принимающие единственный параметр типа *Тестирование.T.
Создадим файл main_test.go в той же директории:
package main
import "Тестирование"
func TestGreeting(t *Тестирование.T) {
input := "Alice"
want := "Hello, Alice!"
// Для изоляции логики вынесем формирование приветствия в отдельную функцию
got := buildGreeting(input)
if got != want {
t.Errorf("buildGreeting(%q) = %q, want %q", input, got, want)
}
}
// Вспомогательная функция, которую будем тестировать
func buildGreeting(name string) string {
return "Hello, " + name + "!"
}
Изменим main.go, вынеся логику в buildGreeting:
// ... импорты ...
func main() {
fmt.Print("Введите ваше имя: ")
reader := bufio.NewReader(os.Stdin)
name, err := reader.ReadString('\n')
if err != nil {
fmt.Fprintf(os.Stderr, "Ошибка ввода: %v\n", err)
os.Exit(1)
}
name = name[:len(name)-1]
greeting := buildGreeting(name)
fmt.Println(greeting)
}
func buildGreeting(name string) string {
return "Hello, " + name + "!"
}
Теперь команда go test в корне проекта выполнит все тесты:
$ go test
PASS
ok hello-go 0.001s
При неудаче вывод включает диагностику, номер строки и описание ошибки. Команда go test -v включает подробный режим с именами тестов.
Тесты не требуют внешних зависимостей, запускаются параллельно, поддерживают бенчмарки (BenchmarkXxx), примеры (ExampleXxx) и coverage-анализ (go test -cover). Это делает их неотъемлемой частью процесса разработки.
Инструменты сопровождения
Go поставляется с набором утилит, обеспечивающих единообразие и качество кода.
-
go fmt— форматирование исходного кода по каноническим правилам: отступы табуляцией, пробелы вокруг операторов, выравнивание импортов, перенос длинных строк. Работает рекурсивно по всем.go-файлам в текущем каталоге и подкаталогах. Не требует настройки — правила фиксированы и едины для всей экосистемы. -
go vet— статический анализ на наличие подозрительных конструкций: неиспользуемых переменных, небезопасных преобразований, ошибок форматирования вfmt.Printf, несинхронизированного доступа к разделяемым данным и других типичных антипаттернов. Выполняется как частьgo test, но может запускаться отдельно. -
go doc— генерация и просмотр документации прямо в терминале. Например,go doc fmt.Printlnвыводит сигнатуру и описание функции. Документация извлекается из комментариев, непосредственно предшествующих объявлению и начинающихся с имени сущности:
// buildGreeting constructs a greeting message for the given name.
// It appends name to the prefix "Hello, " and adds trailing exclamation mark.
func buildGreeting(name string) string { ... }
Такой комментарий будет включён в go doc . buildGreeting.
gofmt -d— показывает, какие изменения внесётgo fmt, без модификации файлов. Полезно для проверки перед коммитом.
Эти инструменты интегрированы в редакторы (например, VS Code автоматически запускает go fmt при сохранении), что делает соблюдение стандартов невидимым и необременительным.
Зависимости и управление версиями
Хотя первая программа не требует внешних библиотек, понимание системы зависимостей критично для дальнейшего роста.
Когда в коде появляется import "github.com/someuser/somepackage", Go при первом запуске go run или go build:
- Ищет пакет в кеше модулей (
$GOPATH/pkg/modили$GOCACHE). - Если отсутствует — загружает репозиторий через
git,hgилиsvn. - Выбирает версию, если указан тег (например,
v1.2.3), или последний коммит в ветке по умолчанию. - Записывает точную версию в
go.modи контрольную сумму вgo.sum. - Компилирует пакет и кэширует объектный файл.
Добавим лёгкую зависимость — пакет для цветного вывода github.com/fatih/color:
go get github.com/fatih/color@v1.17.0
Файл go.mod обновится:
module hello-go
go 1.23
require github.com/fatih/color v1.17.0
Файл go.sum получит хеши содержимого — это гарантия неизменности зависимостей между сборками.
Изменённый main.go:
package main
import (
"bufio"
"fmt"
"os"
"github.com/fatih/color"
)
func main() {
fmt.Print("Введите ваше имя: ")
reader := bufio.NewReader(os.Stdin)
name, err := reader.ReadString('\n')
if err != nil {
color.Red("Ошибка ввода: %v", err)
os.Exit(1)
}
name = name[:len(name)-1]
greeting := buildGreeting(name)
color.Green(greeting)
}
func buildGreeting(name string) string {
return "Hello, " + name + "!"
}
Теперь приветствие выводится зелёным цветом в терминале, поддерживающем ANSI-коды. Важно: зависимость добавлена только в go.mod, не в vendor/. Go по умолчанию использует глобальный кеш, но при необходимости можно создать локальную копию зависимостей командой go mod vendor.
См. также
Другие статьи этого же раздела в боковом меню (как на странице «О разделе»). Эти принципы проявляются уже на уровне архитектуры языка. Go компилируется в машинный код без промежуточного байткода, что обеспечивает выполнение, сравнимое по скорости с C/C++, при этом устраняя… Фундамент для начинающего программиста - что повторить, как работать, чего ожидать. Набор советов, правил, принципов и обычаев в разработке на этом языке. 3. Отсутствие исключений и единый стиль обработки ошибок. Возврат ошибки как второго значения — идиома Go — обеспечивает явность, но ведёт к многоуровневой прокрутке if err = nil return err . Попытки… Все эти инструменты образуют единый, согласованный рабочий процесс. Они минимизируют необходимость в сторонних утилитах, снижают порог входа для новых разработчиков и обеспечивают высокую скорость… Кавычки, точки, запятые, скобки и прочие знаки препинания. Предопределённые идентификаторы не являются ключевыми словами, но имеют специальное значение в языке. Их можно переопределить в локальной области видимости, но делать это не рекомендуется. Набор функций, которые включены в стандартную библиотеку языка. Интерфейсы в Go — это контракты на поведение. Они определяют, что объект может делать. Это смещает фокус с классификации сущностей на описание их возможностей — что соответствует духу композиционного… Go вводит конкурентность через встроенные синтаксические конструкции и правила выполнения. Ниже рассматриваются основные направления практического применения Go, объяснённые через призму его технических характеристик и требований реальных инфраструктур. Типизация, набор правил определения типа данных значений языка.Основы языка Go
Что требуется знать перед началом изучения языка программирования Go
Рекомендации по разработке на Go
История языка Go
Экосистема приложений на Go
Синтаксис и пунктуация в Go
Ключевые слова языка Go
Встроенные функции и пакеты Go
Особенности языка Go
Синтаксические конструкции Go
Области применения Go
Типы данных и объявление переменных в Go