GraphLMS

Основы
Начать
Глава 01 · Основы~8 мин чтения

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

О чём глава

Go — это язык, на котором пишут серверы, утилиты командной строки и большие распределённые системы. В этой главе мы не будем городить ничего сложного: напишем самую первую программу, разберём, из каких обязательных частей она состоит, и научимся выводить текст и значения на экран. К концу главы вы поймёте, что происходит, когда вы запускаете программу командой go run, чем она отличается от go build, и как печатать не только «Hello, world», но и числа, строки и даже типы переменных.

Никакого предыдущего опыта с Go не нужно. Если вы вообще никогда не писали код — тоже ничего страшного, мы идём медленно.

Что вообще такое Go

Прежде чем писать, полезно понять три вещи о языке. Они объясняют, почему код выглядит именно так и почему ведёт себя предсказуемо.

Go компилируемый. Это значит, что ваш текст программы (исходный код) сначала переводится в машинный код — готовый исполняемый файл, который процессор понимает напрямую. В отличие от языков вроде Python или JavaScript, где код читает и исполняет отдельная программа-интерпретатор «на лету», у Go есть отдельный шаг — компиляция. Платим мы за это секундами ожидания при сборке, а получаем быстрый запуск и единый файл, который можно просто скопировать на сервер и запустить.

Go статически типизированный. У каждого значения есть тип, известный заранее: число, строка, логическое значение и так далее. Компилятор проверяет типы ещё до запуска. Если вы попробуете сложить строку с числом по ошибке, программа просто не соберётся — и вы узнаете о проблеме сразу, а не когда она уже «выстрелит» у пользователя. Это очень удобно: компилятор работает вашим первым ревьюером.

В Go есть сборщик мусора (garbage collector). Память под данные выделяется автоматически, и когда данные больше не нужны, среда исполнения сама их освобождает. Вам не нужно вручную «возвращать» память, как в C. Это снимает целый класс ошибок и даёт сосредоточиться на логике.

Держите эти три свойства в голове — они будут объяснять поведение Go во всех следующих главах.

Скелет программы: package main и func main

Любая исполняемая Go-программа начинается с одного и того же каркаса. Вот он целиком:

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

Разберём по строчкам, потому что здесь нет ни одной лишней.

package main — это объявление пакета. Пакет — это набор файлов, которые живут вместе и образуют одну логическую единицу. Имя main особенное: оно говорит компилятору, что из этого пакета нужно собрать запускаемую программу, а не библиотеку. Если пакет называется иначе — это библиотека, которую подключают к другим программам.

import "fmt" подключает стандартный пакет fmt (от слова format). Именно в нём лежат функции для вывода текста, которыми мы пользуемся ниже. Чтобы пользоваться чужим кодом, его нужно сначала импортировать.

func main() — это функция с именем main. Она тоже особенная: с неё начинается выполнение программы. Когда вы запускаете собранный файл, Go находит main в пакете main и вызывает её. Всё, что между фигурными скобками { }, — тело функции, то есть инструкции, которые выполнятся по порядку сверху вниз.

Запомните пару: пакет main + функция main. Без этой пары запускать нечего — компилятор не будет знать, откуда стартовать.

Запускаем: hello world

Попробуем вживую. Нажмите «запустить» под примером — программа выполнится прямо в браузере.

package main
 
import "fmt"
 
func main() {
	fmt.Println("Привет, Go!")
	fmt.Println("Это моя первая программа.")
}

fmt.Println печатает то, что вы ей передали, и переводит строку (суффикс ln — от line). Каждый вызов — с новой строки. Если передать несколько значений через запятую, они напечатаются в одну строку через пробел:

package main
 
import "fmt"
 
func main() {
	fmt.Println("сумма", 2, "и", 3, "равна", 2+3)
}

Обратите внимание: мы смешали строки и числа в одном вызове, и Go спокойно их вывел. Println умеет печатать значения любого типа.

go run и go build

На своём компьютере (не в браузере) вы запускаете Go двумя основными командами. Разница между ними простая, но важная.

go run . берёт исходный код, компилирует его во временный файл, тут же запускает и выкидывает результат. Удобно во время разработки: правишь код, набираешь go run ., сразу видишь итог. Готового файла после этого не остаётся.

go run .

go build компилирует код в настоящий исполняемый файл и кладёт его рядом — например app (или app.exe на Windows). Этот файл можно запускать сколько угодно раз без повторной компиляции и переносить на другие машины.

go build
./app

Интуиция такая: go run — это «собери и запусти разок, прямо сейчас», а go build — «собери мне готовую программу, я её сохраню и буду пользоваться». В песочнице этого учебника за вас всё запускается автоматически, поэтому команды набирать не нужно — но на реальном проекте вы будете печатать именно их.

Форматированный вывод: fmt.Printf

Println хорош для быстрой печати, но часто нужно собрать строку по шаблону: вставить число в середину предложения, показать значение в нужном виде. Для этого есть fmt.Printf.

Printf принимает строку-шаблон с глаголами форматирования — это значки вроде %d или %s, на место которых подставляются ваши значения. Сами значения идут следующими аргументами, по порядку. Важная деталь: Printf не переводит строку автоматически, поэтому в конце шаблона обычно ставят \n — символ перевода строки.

Вот основные глаголы, которые покрывают почти всё на старте:

  • %d — целое число (от decimal);
  • %s — строка (от string);
  • %v — значение в его «обычном» виде, подходит почти для чего угодно (от value);
  • %T — тип значения, а не само значение (от type).

Посмотрим на %s и %d в деле:

package main
 
import "fmt"
 
func main() {
	name := "Аня"
	age := 25
	fmt.Printf("Меня зовут %s, мне %d лет.\n", name, age)
	fmt.Printf("Через год будет %d.\n", age+1)
}

Здесь %s заменился на строку "Аня", а оба %d — на числа. Порядок подстановки строго слева направо: первый глагол берёт первый аргумент после шаблона, второй — второй, и так далее.

Если вы не уверены, какой глагол подойдёт, или просто хотите «напечатай как есть», берите %v. Он универсальный:

package main
 
import "fmt"
 
func main() {
	count := 7
	label := "товаров"
	price := 19.99
	fmt.Printf("В корзине %v %v на сумму %v\n", count, label, price)
}

%v напечатал и число 7, и строку товаров, и дробное 19.99 — каждое в естественном виде. На практике %v — самый частый глагол: с него удобно начинать, а на %d/%s переходить, когда нужен контроль над форматом.

Узнаём тип значения: %T

Поскольку Go статически типизированный, у каждого значения есть конкретный тип. Иногда полезно посмотреть на него глазами — особенно пока вы только осваиваетесь и хотите проверить себя. Для этого есть глагол %T.

package main
 
import "fmt"
 
func main() {
	fmt.Printf("%T\n", 42)
	fmt.Printf("%T\n", "привет")
	fmt.Printf("%T\n", 3.14)
	fmt.Printf("%T\n", true)
}

Вывод покажет: целое число имеет тип int, текст — string, дробное — float64, а true/falsebool. Это те базовые типы, с которыми вы будете работать постоянно. Подробно про них — в следующих главах; пока достаточно знать, что %T всегда подскажет, с чем вы имеете дело.

Комментарии

Комментарии — это заметки в коде для людей. Компилятор их полностью игнорирует, а вы (и ваши коллеги) используете, чтобы объяснить, почему написано именно так. В Go два вида комментариев.

Однострочный начинается с // и идёт до конца строки. Многострочный заключается между /* и */.

package main
 
import "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 и как с ними работать осознанно.