Совсем другая книга про Go

Совсем другая книга про Go #

“Маленькие книги” Карла Сегуина – лучшее, что только есть в жанре “быстрого старта”. В них только самое главное. Подробности, в которых так легко утонуть поначалу, так же легко восполнить правильно заданными вопросами и самостоятельно найденными ответами.

Когда-то “Маленькая книга про Go” вдохновила меня написать несколько других “маленьких книг” на темы, для которых книг Карла не было. А потом, когда я сам пришел к Go и вернулся к этой книге, вдруг оказалось, что многое изменилось. Это будет совсем другая книга про Go. Не судите строго.

Go это “C++ для маленьких”. Звучит странно, но именно такие ожидания были в Google от нового языка программирования. Простой, производительный, безопасный. В итоге получился компактный компилируемый язык со строгой типизацией, автоматическим управлением памятью, поддержкой многозадачности, модульной организацией кода и своим менеджером пакетов, дающем доступ к большой библиотеке готовых решений, позволяющих даже новичку быть достаточно эффективным.

Он хорош во всем, кроме кривой обучения. В качестве первого языка он не то, чтобы ужасен, но… здесь с самого начала надо знать то, что обычно изучается в последнюю очередь. Рассказывать о нем – настоящий вызов. Но я попробую. Опять же, не судите строго.

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

У пресловутого “Hello, World” несколько целей. Во-первых, убедиться, что всё нужное для работы установлено и настроено. Во-вторых, как на первом свидании, увидеть в действии и оценить перспективы. А в-третьих, преодолеть психологический барьер: программа уже написана и работает — осталось только понять, как она это делает.

В любом текстовом редакторе создайте файл hello.go:

package main

func main() {
    println("Hello, world!")
}

Выполните его:

$ go run hello.go
Hello, world!

Или скомпилируйте и выполните:

$ go build hello.go
$ ./hello
Hello, world!

Вот, собственно и все. Но простота первой программы обманчива – следующая ступенька больше похожа на бетонный забор, для которого нам потребуется отдельная “лесенка”.

Немного теории #

Любое сложное действие можно разделить на простые, те – на еще более простые и так далее, до базовых машинных команд. Это – декомпозиция, основа всего программирования.

Единицей декомпозиции в Go являются функции. Но не те, что в математике (или, скажем, в языке программирования Haskell). Они получают аргументы и возвращают значения, но если “там” значение зависит только от аргументов, то “здесь” это скорее подпрограммы – их нельзя, однажды вычислив, автоматически заменять готовым значением при повторном вызове.

Где-то между решением шахматного этюда и машинным кодом проходит граница, разделяющая интеллектуальный труд программиста и механическую работу компилятора. Это – уровень абстракции, он у каждого языка свой. Все, что ниже этого уровня, компилятор превратит в машинный код сам.

Пакеты #

Пакет это единица компиляции. Программа собирается из пакетов, как из кубиков. И, хотя в пакет можно собрать несколько функций, единица компиляции – сущность атомарная, в конечную программу попадают не вызываемые функции, а весь пакет целиком.

Модули #

Вся стандартная библиотека состоит из модулей. Для того, чтобы вызвать функцию из какого-нибудь пакета, нужно сначала явным образом импортировать пакет с ней. Именно так компилятор будет знать, какие именно пакеты нужно собрать в программе.

Пакеты стандартной библиотеки вызывают функции друг друга и это не составляет проблемы – при обновлении стандартной библиотеки все изменения в пакетах остаются согласованными. Другое дело, когда используются сторонние библиотеки, которые могут измениться в любой момент. Для этого в Go есть модули.

Модуль – единица организации кода. В модуль входит один или несколько пакетов и у него есть версия. Это спасает в ситуациях, когда работающая программа перестает собираться из-за того, что какой-то из пакетов за это время обновится и потерял совместимость.

Программа #

Скомпилированная программа это исполнимый двоичный файл для ваших операционной системы (Windows, Linux, MacOS, iOS, Android…) и процессора (тоже с десяток вариантов). Но можно скомпилировать под любую другую пару. Этот файл содержит пакет main и все-все нужные пакеты. Он единый и не нуждается ни в каких внешних зависимостях – скопируй и запускай. Технически любая программа – результат сборки модуля, среди пакетов которого есть main с функцией main. Именно с этой функции начинается выполнение программы.

Вторая “первая” программа #

Теперь построим “настоящую” программу – с функциями, пакетами и модулями.

$ mkdir demo
$ cd demo
$ go mod init demo

Появился файл go.mod. Это и есть модуль. Вот его содержимое:

module demo

go 1.24.6

Мы видим название модуля и используемую версию go. Создадим файлы для модуля main.

hello.go (имена файлов не имеют никакого значения)

package main
import hitchhiker "demo/UA"

func main() {
	hello_en()
	hello_de()
	hitchhiker.Answer()
}

hello.go (один пакет можно разбить на несколько файлов, главное – указать общее имя в первой строке)

package main
import "fmt"

func hello_en () {
	fmt.Println ("Welcome!")
}

func hello_de () {
	fmt.Println ("Willkommen!")
}

Создадим вложенный пакет UA

$ mkdir UA
$ cd UA

hitchhiker.go

package hitchhiker
import "fmt"

func Answer() {
	fmt.Println("Ultimate answer is", 42)
}

Компилируем и запускаем:

$ go build
$ ./demo
Welcome!
Willkommen!
Ultimate answer is 42

Поздравляю! Пройден самый крутой участок кривой обучения. По сравнению с ним, все остальное будет просто прогулкой.