Содержание урока · 11 разделов
- Переменные в Go: объявление через var
- Нулевые значения: пусто — это не мусор
- Нулевые значения основных типов
- Короткое объявление: переменные через :=
- Константы const: значения на этапе компиляции
- iota: автоматические перечисления
- Область видимости: где живёт переменная
- Как называть переменные в Go
- Песочница урока
- Коротко: что запомнить
- Вопросы и ответы
Переменные в Go: объявление через var#
Самый явный способ завести переменную в Go — ключевое слово var. После имени указывается тип, а начальное значение можно дать сразу или оставить на потом:
var age int — просто объявление, значение появится позже. var price float64 = 199.5 — объявление с инициализацией. var title = "Курс Go" — тип можно опустить: компилятор выведет его сам по правой части.
На уровне пакета (вне любой функции) работает только var — короткое объявление := там запрещено. Это не каприз: переменные уровня пакета живут всю жизнь программы, и хочется, чтобы их объявления бросались в глаза. Внутри функции обычно пишут :=, а var оставляют для случаев, когда тип важнее значения или когда переменную объявляют заранее, чтобы присвоить позже.
Несколько переменных одного типа объявляются в одну строку: var x, y int = 1, 2. А ещё var-объявления принято группировать в блоки — особенно на уровне пакета, где их бывает много.
Разные способы объявить переменную
GOpackage main
import "fmt"
// Группировка var-объявлений на уровне пакета
var (
appName = "ГоМастер"
version = 1
)
func main() {
var age int // без значения — нулевое
var price float64 = 199.5 // тип указан явно
var title = "Курс Go" // тип выведен компилятором
count, limit := 10, 100 // короткое объявление двух переменных
fmt.Println(appName, version)
fmt.Println(age, price, title, count, limit)
}Переменные appName и version объявлены на уровне пакета, поэтому доступны в main без передачи. age не инициализирована — в ней лежит нулевое значение для int, то есть 0. В строке count, limit := 10, 100 короткое объявление создаёт сразу две переменные.
Нулевые значения: пусто — это не мусор#
В C или C++ необъявленная переменная содержит мусор — что-то, что было в памяти до неё. В Go иначе: каждая объявленная переменная сразу получает нулевое значение своего типа. Для чисел это 0, для строк — пустая строка, для bool — false, для указателей, срезов и карт — nil.
Это решает целый класс ошибок: вы никогда не прочитаете «случайное» значение, только предсказуемое. Отсюда идиома «объявляй переменную заранее, а присваивай в ветке if» — она безопасна, потому что до присвоения там лежит известное нулевое значение.
Но есть и обратная сторона: Go не отличает «я забыла инициализировать» от «здесь сознательно пусто». Если переменная должна была получить значение из внешнего мира, нулевое значение замаскирует проблему вместо того, чтобы её показать. Поэтому в важных местах значение проверяют сразу после получения, а не надеются на память.
Нулевые значения основных типов#
| Тип | Нулевое значение | Пример |
|---|---|---|
| int, int64, uint и другие целые | 0 | var n int // 0 |
| float64, float32 | 0.0 | var p float64 // 0 |
| string | "" (пустая строка) | var s string // "" |
| bool | false | var ok bool // false |
| указатель, срез, карта, интерфейс | nil | var ptr *int // nil |
Go считает неиспользуемую переменную ошибкой компиляции, а не предупреждением. count := 10 без единого использования ниже — и сборка падает с «declared and not used». Это раздражает ровно до первого раза, когда оно спасает вас от мёртвого кода. Если переменная реально не нужна — уберите объявление или используйте пустой идентификатор _.
Короткое объявление: переменные через :=#
Внутри функций Go позволяет объявить переменную без var и без типа: x := 5. Это главная рабочая лошадка языка — так пишется 90% локальных переменных. Тип выводится из правой части, и код читается заметно легче, чем с явными типами.
У := есть два жёстких правила. Первое: работает только внутри функций. Второе: слева должен быть хотя бы один новый идентификатор. Это позволяет переиспользовать существующие переменные, объявляя соседнюю: x, err := do() в цикле — err новая, x переиспользуется, компилятор доволен.
Распространённая ошибка новичков — наивная попытка переприсвоить через :=. Строка x := 5 там, где x уже объявлена в той же области видимости и других новых переменных слева нет, даёт ошибку «no new variables on left side of :=». В таких случаях нужен обычный =.
Лично я на уровне пакета пишу var, внутри функций — :=, и не считаю это эстетством: var подчёркивает долгоживущие штуки, := держит короткие локальные значения на виду.
Проверьте себя: где работает :=
Что произойдёт при попытке написать `n := 10` на уровне пакета (вне всех функций)?
Что выведет код?
Прочитайте код и выберите, что он выведет:
package main
import "fmt"
func main() {
x := 5
y := x
x = 10
fmt.Println(x, y)
}Константы const: значения на этапе компиляции#
Константы в Go объявляются словом const и отличаются от переменных принципиально: их значение вычисляется на этапе компиляции и не может измениться. В константу можно положить число, строку или bool — но не результат вызова функции, потому что функции выполняются уже во время работы программы.
Зачем это нужно? Во-первых, гарантия: если вы видите const, значит, значение нигде не перепишется — компилятор проследит. Во-вторых, скорость: константа встраивается в код как литерал, переменной в памяти для неё может не быть вовсе. В-третьих, константы могут быть нетипизированными: const N = 10 — это не int и не float64, а «число 10 вообще», которое подстраивается под контекст. Её можно присвоить и переменной типа int, и float64 без преобразований.
По конвенции простые константы уровня пакета пишут в camelCase (maxUsers), а экспортируемые — с заглавной буквы (MaxUsers). Все заглавные буквы подробно разберём в уроке про видимость имён.
Константы на практике
GOpackage main
import "fmt"
const Pi = 3.14159
const maxUsers = 100 // приватная константа пакета
func main() {
radius := 2.0
length := 2 * Pi * radius
fmt.Println("Длина окружности:", length)
const greeting = "Привет"
fmt.Println(greeting, maxUsers)
}Pi и maxUsers объявлены на уровне пакета, greeting — внутри main. Обратите внимание: Pi нетипизированная, поэтому 2 Pi radius считается во float64 без каких-либо преобразований. Попробуйте изменить Pi в редакторе — получите ошибку компиляции.
Строка Pi = 3 вызывает ошибку компиляции «cannot assign to Pi». Это не предупреждение и не поведение во время выполнения — программа просто не соберётся. Если значение может измениться по ходу работы, это не константа, а переменная: пишите var.
iota: автоматические перечисления#
Когда нужен набор связанных констант — дни недели, уровни логирования, размеры файлов — Go предлагает приём, которого нет в других языках: iota. Это счётчик внутри блока const, начинающийся с 0 и увеличивающийся на каждой строке объявления.
Классика жанра: const ( Mon = iota; Tue; Wed ) — Mon получит 0, Tue унаследует выражение и получит 1, Wed — 2. Повторять = iota в каждой строке не нужно: предыдущее выражение применяется автоматически.
Настоящая сила — в комбинации с арифметикой. Знаменитая формула размеров памяти: KB = 1 << (10 * iota) даёт 1024 для KB, 1048576 для MB и так далее — без единого ручного числа. Так стандартный пакет time объявляет единицы времени: Nanosecond = 1, а дальше Microsecond = 1000 Nanosecond, Millisecond = 1000 Microsecond — константы ссылаются друг на друга по цепочке.
Важный нюанс: iota сбрасывается в 0 в каждом новом блоке const. И если строка в блоке пропущена через подчёркивание, iota всё равно на неё потратится — счётчик считает строки, а не созданные константы.
Песочница: iota в деле
Запустите код и убедитесь, что iota считает строки, а константы могут ссылаться друг на друга. Добавьте в первый блок Sat после Fri — она должна получить 5. Затем попробуйте вставить в начало первого блока строку `_` (пустой идентификатор) и посмотрите, как сдвинутся все значения.
Запустите код, чтобы увидеть вывод.
Область видимости: где живёт переменная#
Объявленная переменная доступна не везде, а внутри своей области видимости — блока кода, в котором она объявлена. Переменная уровня пакета видна во всём пакете, объявленная внутри функции — только в этой функции, объявленная внутри цикла for или блока if — только там.
Практическое следствие: чем короче область видимости, тем меньше мест, откуда переменную можно испортить. Поэтому локальную переменную не выносят на уровень пакета «на всякий случай» — если она нужна только внутри main, объявляйте её внутри main.
С этим связан и приём затенения (shadowing): внутренняя переменная с тем же именем перекрывает внешнюю. Он легален и иногда полезен (например, tmp := x внутри маленького блока), но чаще приводит к трудноуловимым багам: вы меняете «не ту» переменную. Чтобы не путаться, дайте внутренним переменным собственные имена — затенение в Go не запрещено, но в грамотном коде встречается нечасто.
Как называть переменные в Go#
Го-сообщество договорилось о правилах, и линтеры за ними следят. Имена пишутся в camelCase: maxRetryCount, userName. Сокращения допустимы, если они общеприняты: url, id, db. Длинное имя оправдано только тогда, когда переменная живёт долго; для счётчика цикла i лучше не придумаешь.
Заглавная первая буква — это не стиль, а механика экспорта: MaxUsers виден из других пакетов, maxUsers — нет. Константы в Go, в отличие от C, не пишут капсом: DEFAULT_TIMEOUT в Го-коде выглядит чужеродно, правильнее defaultTimeout или DefaultTimeout.
И последнее: имя должно отвечать на вопрос «что здесь лежит», а не «какого это типа». userNames []string читается лучше, чем strArr2 — и через полгода тоже.
Соберите код: константа и переменная
Впишите пропущенные ключевые слова и тип так, чтобы программа скомпилировалась и напечатала длину окружности.
Соберите программу из строк
Перетащите строки в правильном порядке: package, импорт, константа, функция, вывод.
Строки
Ваша программа
Нажимайте на строки слева, чтобы собрать программу
Карточки: var, const, iota
Песочница урока
Редактируйте код и запускайте его прямо здесь — интерпретатор работает в браузере, ничего устанавливать не нужно. За каждый запуск — +2 XP.
Запустите код, чтобы увидеть вывод.
Коротко: что запомнить
- var — явное объявление с типом; на уровне пакета работает только он.
- Каждая переменная получает нулевое значение своего типа — мусора в памяти не бывает.
- Внутри функций пишут :=: тип выводится сам, но слева нужен хотя бы один новый идентификатор.
- Неиспользуемая переменная — ошибка компиляции, а не предупреждение.
- const — значение на этапе компиляции; константу нельзя переприсвоить.
- iota — автосчётчик внутри блока const: идеален для перечислений и размеров памяти.
- Область видимости ограничена блоком; заглавная буква имени означает экспорт из пакета.
Домашнее задание
Напишите программу, которая объявляет на уровне пакета константу секундВМинуте = 60 и три переменные с нулевыми значениями (int, string, bool). В main присвойте им осмысленные значения (например, часы = 2, имя = "Алёна", isDone = true), а затем выведите их через fmt.Println. Добавьте блок const с тремя днями недели через iota и выведите их номера.
Личные заметкиПусто
Вопросы и ответы по уроку
Чем := отличается от var и когда что использовать?
Оба объявляют переменные, но := короче и выводит тип сам, работает только внутри функций. var нужен на уровне пакета, при явном указании типа и в var-блоках. Внутри функций большинство Го-разработчиков пишут :=, оставляя var для случаев, когда тип важнее значения.
Что будет, если присвоить значение константе?
Программа не скомпилируется: Go выдаст ошибку «cannot assign to». Это проверка на этапе компиляции, а не во время работы — константа гарантирует неизменность сильнее, чем, например, const в JavaScript.
Почему нельзя объявить неиспользуемую переменную?
Go рассматривает неиспользуемую переменную как признак ошибки в логике и отказывается собирать программу. Это философия языка: ошибки должны ловиться как можно раньше. Временную переменную можно «проглотить» пустым идентификатором _.
Термины урока
Ключевые понятия из этого урока — короткие определения в глоссарии. Наведите на термин, чтобы вспомнить, откройте карточку для подробностей и ссылки на практику.
Изучайте дальше
Следующая ступень учебного пути
Продвинутые возможности Go: от замыканий до рефлексии
Продвинутый курс Go: замыкания, defer, горутины, каналы, контекст, интерфейсы, дженерики, рефлексия, тесты и JSON. 25 уроков с практикой в браузере.
- Писать и читать конкурентный код: go-функции, каналы, select
- Управлять жизненным циклом операций через context
- Проектировать API на интерфейсах
- Использовать дженерики без злоупотребления