Go спроектирован так, чтобы код читался одинаково у всех. В языке мало конструкций, почти нет «синтаксического сахара», а компилятор ругается на лишнее — например, на неиспользуемую переменную или импорт. Эта статья — первое знакомство с тем, как выглядит программа на Go: из чего она собрана, как объявляются переменные и типы, и как работают базовые управляющие конструкции.
Простота как принцип
Главная идея Go — читаемость важнее краткости. Авторы языка сознательно отказались от многих привычных по другим языкам возможностей: нет наследования классов, нет исключений в привычном виде, нет тернарного оператора, есть только один цикл — for. Меньше способов написать одно и то же — меньше споров и проще читать чужой код.
Из этого следует строгость компилятора. Объявил переменную и не использовал — ошибка компиляции, а не предупреждение. Импортировал пакет «на будущее» — тоже ошибка. Поначалу это раздражает, но быстро приучает не оставлять мусора.
Пакеты и func main
Любой файл Go начинается с объявления пакета. Пакет — это единица организации кода: набор файлов в одной директории, которые видят внутренние имена друг друга.
Программа, которую можно запустить, живёт в пакете main и содержит функцию main — точку входа:
package main
import "fmt" // пакет стандартной библиотеки для ввода-вывода
func main() {
fmt.Println("Привет, Go") // печать строки в консоль
}
Несколько важных деталей сразу:
package main+func main()— это то, что делает программу исполняемой. Библиотеки используют другое имя пакета и функцииmainне имеют.importподключает другие пакеты.fmt— часть стандартной библиотеки.- Видимость определяется регистром буквы. Имя с заглавной буквы (
Println) экспортируется — доступно из других пакетов. Имя со строчной буквы видно только внутри своего пакета. Отдельных ключевых слов вродеpublic/privateнет.
Объявление переменных: var и :=
Переменную можно объявить двумя способами. Полная форма — через var:
var age int = 30 // тип указан явно
var name = "Анна" // тип выведен компилятором из значения (string)
var active bool // без значения — переменная получит нулевое значение
Короткая форма — оператор :=. Он одновременно объявляет переменную и присваивает значение, тип выводится автоматически:
age := 30 // int
name := "Анна" // string
active := true // bool
Короткая формула: := работает только внутри функций; var можно использовать и на уровне пакета. На практике внутри функций почти всегда пишут :=, а var оставляют для случаев, когда нужно явно задать тип или объявить переменную без начального значения.
Тип в Go указывается после имени — age int, а не int age. Это непривычно, но читается слева направо: «переменная age типа int».
Базовые типы
Набор встроенных типов небольшой и предсказуемый:
var i int // целое, размер зависит от платформы (обычно 64 бита)
var i32 int32 // целое фиксированного размера; есть int8/int16/int64
var u uint // беззнаковое целое
var f float64 // число с плавающей точкой (по умолчанию для дробей)
var b bool // true / false
var s string // строка, неизменяемая последовательность байт (UTF-8)
var r rune // один символ Unicode (псевдоним int32)
Несколько правил, которые экономят время:
- По умолчанию для целых берут
int, для дробных —float64. Конкретные размеры (int32,int64) — когда это важно для формата данных или экономии памяти. - Go не делает неявных преобразований между числовыми типами. Сложить
intиint64напрямую нельзя — нужно явно преобразовать:int64(x) + y. Это убирает целый класс скрытых ошибок. stringнеизменяема: нельзя поменять символ по индексу. Чтобы собрать строку из частей, используют конкатенацию или специальные средства из стандартной библиотеки.
Нулевые значения (zero values)
В Go нет понятия «неинициализированная переменная». Если объявить переменную без значения, она получит нулевое значение своего типа:
var n int // 0
var f float64 // 0.0
var b bool // false
var s string // "" (пустая строка)
var p *int // nil (для указателей, срезов, map, каналов, функций, интерфейсов)
Это сознательное решение: переменная всегда в осмысленном состоянии, «мусора» из памяти не бывает. Нулевые значения часто делают типы готовыми к работе сразу — например, нулевое значение многих структур из стандартной библиотеки уже можно использовать без дополнительной настройки.
Короткая формула: объявил — уже инициализировано. Думай о нулевом значении как о части дизайна типа, а не как о «пустышке».
Константы
Константы объявляются через const и вычисляются на этапе компиляции. Менять их нельзя:
const Pi = 3.14159
const greeting = "привет"
const (
StatusOK = 200
StatusError = 500
)
Числовые константы в Go нетипизированы, пока их не присвоят переменной. Благодаря этому const Pi = 3.14159 спокойно используется и там, где ждут float64, и там, где ждут float32 — без явного преобразования. Для перечислений в Go используют конструкцию iota, но это тема для более продвинутого разбора.
Управляющие конструкции
if
Скобки вокруг условия не нужны, фигурные скобки обязательны:
if age >= 18 {
fmt.Println("совершеннолетний")
} else {
fmt.Println("нет")
}
Удобная особенность — короткий оператор перед условием. Можно объявить переменную прямо в if, и она будет видна только внутри ветвей:
if n := len(name); n > 0 {
fmt.Println("длина имени:", n)
}
// здесь n уже недоступна
for — единственный цикл
В Go только один цикл — for, но он покрывает все случаи. Классическая форма со счётчиком:
for i := 0; i < 3; i++ {
fmt.Println(i)
}
Форма «пока условие истинно» (аналог while из других языков):
i := 0
for i < 3 {
i++
}
Бесконечный цикл — просто for { }. А для перебора коллекций есть for ... range:
names := []string{"Анна", "Борис"}
for index, value := range names {
fmt.Println(index, value)
}
switch
switch в Go мощнее, чем привычный по другим языкам. По умолчанию нет «проваливания» в следующую ветку — break писать не нужно:
switch day {
case "сб", "вс":
fmt.Println("выходной")
case "пт":
fmt.Println("почти выходной")
default:
fmt.Println("рабочий день")
}
switch можно писать вообще без выражения — тогда ветки case содержат условия. Это чистая замена длинной цепочки if/else:
switch {
case age < 18:
fmt.Println("несовершеннолетний")
case age < 65:
fmt.Println("взрослый")
default:
fmt.Println("пенсионер")
}
Почему в Go нет тернарного оператора
В большинстве языков есть запись вида condition ? a : b. В Go её намеренно нет. Авторы посчитали, что вложенные тернарники быстро становятся нечитаемыми, а одна явная конструкция лучше двух способов делать выбор.
Вместо тернарника пишут обычный if:
label := "нет"
if age >= 18 {
label = "да"
}
Чуть длиннее — зато читается однозначно. Это хорошая иллюстрация всей философии языка: меньше способов, больше предсказуемости.
Коротко
- Программа на Go начинается с
package; исполняемая программа — этоpackage mainи функцияfunc main(). - Видимость имени определяется регистром первой буквы: заглавная — экспортируется, строчная — видна только в пакете.
- Переменные объявляют через
var(с явным типом) или:=(короткая форма, только внутри функций); тип пишется после имени. - Каждый тип имеет нулевое значение — переменная без явного значения всегда инициализирована (
0,"",false,nil). - Неявных преобразований между числовыми типами нет — преобразовывай явно.
- Цикл один —
for, он же заменяетwhile;switchне «проваливается» и работает без выражения; тернарного оператора нет — используется обычныйif.
Что почитать дальше
- Структуры и методы — как из базовых типов собирать собственные.
- Срезы и map — основные коллекции языка.
- Инструменты Go — go run, go build, форматирование и тесты.