Go — это язык, на котором пишут серверы, утилиты командной строки и большие распределённые системы. В этой главе мы не будем городить ничего сложного: напишем самую первую программу, разберём, из каких обязательных частей она состоит, и научимся выводить текст и значения на экран. К концу главы вы поймёте, что происходит, когда вы запускаете программу командой go run, чем она отличается от go build, и как печатать не только «Hello, world», но и числа, строки и даже типы переменных.
Никакого предыдущего опыта с Go не нужно. Если вы вообще никогда не писали код — тоже ничего страшного, мы идём медленно.
Прежде чем писать, полезно понять три вещи о языке. Они объясняют, почему код выглядит именно так и почему ведёт себя предсказуемо.
Go компилируемый. Это значит, что ваш текст программы (исходный код) сначала переводится в машинный код — готовый исполняемый файл, который процессор понимает напрямую. В отличие от языков вроде Python или JavaScript, где код читает и исполняет отдельная программа-интерпретатор «на лету», у Go есть отдельный шаг — компиляция. Платим мы за это секундами ожидания при сборке, а получаем быстрый запуск и единый файл, который можно просто скопировать на сервер и запустить.
Go статически типизированный. У каждого значения есть тип, известный заранее: число, строка, логическое значение и так далее. Компилятор проверяет типы ещё до запуска. Если вы попробуете сложить строку с числом по ошибке, программа просто не соберётся — и вы узнаете о проблеме сразу, а не когда она уже «выстрелит» у пользователя. Это очень удобно: компилятор работает вашим первым ревьюером.
В Go есть сборщик мусора (garbage collector). Память под данные выделяется автоматически, и когда данные больше не нужны, среда исполнения сама их освобождает. Вам не нужно вручную «возвращать» память, как в C. Это снимает целый класс ошибок и даёт сосредоточиться на логике.
Держите эти три свойства в голове — они будут объяснять поведение Go во всех следующих главах.
Разберём по строчкам, потому что здесь нет ни одной лишней.
package main — это объявление пакета. Пакет — это набор файлов, которые живут вместе и образуют одну логическую единицу. Имя main особенное: оно говорит компилятору, что из этого пакета нужно собрать запускаемую программу, а не библиотеку. Если пакет называется иначе — это библиотека, которую подключают к другим программам.
import "fmt" подключает стандартный пакет fmt (от слова format). Именно в нём лежат функции для вывода текста, которыми мы пользуемся ниже. Чтобы пользоваться чужим кодом, его нужно сначала импортировать.
func main() — это функция с именем main. Она тоже особенная: с неё начинается выполнение программы. Когда вы запускаете собранный файл, Go находит main в пакете main и вызывает её. Всё, что между фигурными скобками { }, — тело функции, то есть инструкции, которые выполнятся по порядку сверху вниз.
Запомните пару: пакет main + функция main. Без этой пары запускать нечего — компилятор не будет знать, откуда стартовать.
fmt.Println печатает то, что вы ей передали, и переводит строку (суффикс ln — от line). Каждый вызов — с новой строки. Если передать несколько значений через запятую, они напечатаются в одну строку через пробел:
На своём компьютере (не в браузере) вы запускаете Go двумя основными командами. Разница между ними простая, но важная.
go run . берёт исходный код, компилирует его во временный файл, тут же запускает и выкидывает результат. Удобно во время разработки: правишь код, набираешь go run ., сразу видишь итог. Готового файла после этого не остаётся.
go run .
go build компилирует код в настоящий исполняемый файл и кладёт его рядом — например app (или app.exe на Windows). Этот файл можно запускать сколько угодно раз без повторной компиляции и переносить на другие машины.
go build./app
Интуиция такая: go run — это «собери и запусти разок, прямо сейчас», а go build — «собери мне готовую программу, я её сохраню и буду пользоваться». В песочнице этого учебника за вас всё запускается автоматически, поэтому команды набирать не нужно — но на реальном проекте вы будете печатать именно их.
Println хорош для быстрой печати, но часто нужно собрать строку по шаблону: вставить число в середину предложения, показать значение в нужном виде. Для этого есть fmt.Printf.
Printf принимает строку-шаблон с глаголами форматирования — это значки вроде %d или %s, на место которых подставляются ваши значения. Сами значения идут следующими аргументами, по порядку. Важная деталь: Printfне переводит строку автоматически, поэтому в конце шаблона обычно ставят \n — символ перевода строки.
Вот основные глаголы, которые покрывают почти всё на старте:
%d — целое число (от decimal);
%s — строка (от string);
%v — значение в его «обычном» виде, подходит почти для чего угодно (от value);
%T — тип значения, а не само значение (от type).
Посмотрим на %s и %d в деле:
package mainimport "fmt"func main() { name := "Аня" age := 25 fmt.Printf("Меня зовут %s, мне %d лет.\n", name, age) fmt.Printf("Через год будет %d.\n", age+1)}
Здесь %s заменился на строку "Аня", а оба %d — на числа. Порядок подстановки строго слева направо: первый глагол берёт первый аргумент после шаблона, второй — второй, и так далее.
Если вы не уверены, какой глагол подойдёт, или просто хотите «напечатай как есть», берите %v. Он универсальный:
%v напечатал и число 7, и строку товаров, и дробное 19.99 — каждое в естественном виде. На практике %v — самый частый глагол: с него удобно начинать, а на %d/%s переходить, когда нужен контроль над форматом.
Поскольку Go статически типизированный, у каждого значения есть конкретный тип. Иногда полезно посмотреть на него глазами — особенно пока вы только осваиваетесь и хотите проверить себя. Для этого есть глагол %T.
Вывод покажет: целое число имеет тип int, текст — string, дробное — float64, а true/false — bool. Это те базовые типы, с которыми вы будете работать постоянно. Подробно про них — в следующих главах; пока достаточно знать, что %T всегда подскажет, с чем вы имеете дело.
Комментарии — это заметки в коде для людей. Компилятор их полностью игнорирует, а вы (и ваши коллеги) используете, чтобы объяснить, почему написано именно так. В Go два вида комментариев.
Однострочный начинается с // и идёт до конца строки. Многострочный заключается между /* и */.
package mainimport "fmt"// main — точка входа в программу.func main() { fmt.Println("работаем") // печатаем строку в консоль /* А это длинное пояснение на несколько строк. */}
Хороший комментарий объясняет замысел, а не пересказывает очевидное. Строка i = i + 1 // увеличиваем i на единицу бесполезна: код и так это говорит. А вот // округляем вверх, иначе теряем копейку при делении — полезен, потому что объясняет причину.
Забыли импортировать fmt. Если вы используете fmt.Println, но не написали import "fmt", программа не соберётся. И наоборот: импортировали пакет, но не пользуетесь им — Go тоже выдаст ошибку. Язык строг к неиспользуемым импортам специально, чтобы код не зарастал мусором.
Перепутали Println и Printf.Println сам переводит строку, а Printf — нет. Если из Printf пропал перевод строки и вывод «слипся», скорее всего вы забыли \n в конце шаблона.
Глагол не совпал с аргументом. Если написать %d и передать туда строку, Go не подставит её молча, но и не уронит сборку — программа соберётся и запустится, просто прямо в напечатанной строке окажется специальная пометка вида %!d(string=привет). Это не падение, а сигнал «глагол и аргумент не подходят друг другу». Узнаете её — сразу поймёте, в чём дело. Когда сомневаетесь в типе, берите %v: он принимает что угодно.
Лишний или недостающий аргумент у Printf. Глаголов в шаблоне должно быть ровно столько, сколько значений вы передаёте. Тут тоже не ошибка сборки, а пометка в самом выводе: лишний аргумент даёт %!(EXTRA string=...) в конце строки, а недостающий — %!d(MISSING) на месте глагола.
Вы написали первую программу, разобрали обязательный каркас package main + func main, научились печатать через Println и форматировать вывод через Printf с глаголами %d, %s, %v, %T. Этого уже достаточно, чтобы видеть результат любой своей программы — а значит, и проверять себя дальше.
В следующей главе мы перейдём к переменным и базовым типам, которые сегодня только мелькнули в %T. Там же станет понятнее, откуда берутся int, string и float64 и как с ними работать осознанно.