Перейти к содержимому
ГоМастер

Поиск по урокам

Найдите урок, курс, термин или раздел

Урок 4 из 25 ~20 мин +50 XP

Переменные и константы

Переменные в Go объявляются иначе, чем в Python или JavaScript: компилятор хочет знать тип каждой штуки заранее и не прощает ни объявленных без дела имён, ни забытых констант. В этом уроке разберём var и короткое объявление :=, выясним, что такое нулевые значения, зачем нужны const и как iota избавляет от ручной нумерации. Все примеры запускаются прямо на странице.

Содержание урока · 11 разделов

Переменные в Go: объявление через var#

Самый явный способ завести переменную в Go — ключевое слово var. После имени указывается тип, а начальное значение можно дать сразу или оставить на потом:

var age int — просто объявление, значение появится позже. var price float64 = 199.5 — объявление с инициализацией. var title = "Курс Go" — тип можно опустить: компилятор выведет его сам по правой части.

На уровне пакета (вне любой функции) работает только var — короткое объявление := там запрещено. Это не каприз: переменные уровня пакета живут всю жизнь программы, и хочется, чтобы их объявления бросались в глаза. Внутри функции обычно пишут :=, а var оставляют для случаев, когда тип важнее значения или когда переменную объявляют заранее, чтобы присвоить позже.

Несколько переменных одного типа объявляются в одну строку: var x, y int = 1, 2. А ещё var-объявления принято группировать в блоки — особенно на уровне пакета, где их бывает много.

Разные способы объявить переменную

GO
package 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 и другие целые0var n int // 0
float64, float320.0var p float64 // 0
string"" (пустая строка)var s string // ""
boolfalsevar ok bool // false
указатель, срез, карта, интерфейсnilvar ptr *int // nil

Короткое объявление: переменные через :=#

Внутри функций 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). Все заглавные буквы подробно разберём в уроке про видимость имён.

Константы на практике

GO
package 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 в редакторе — получите ошибку компиляции.

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 main import "fmt" Pi float64 = 3.14159 func main() { radius = 2.5 length := 2 * Pi * radius fmt.Println("Длина:", length) }

Соберите программу из строк

Перетащите строки в правильном порядке: package, импорт, константа, функция, вывод.

Строки

Ваша программа

Нажимайте на строки слева, чтобы собрать программу

Карточки: var, const, iota

Термин

var x int без значения

нажмите, чтобы перевернуть
Определение

x получает нулевое значение типа: 0 для чисел, "" для строк, false для bool, nil для ссылочных типов.

1 / 6

Песочница урока

Редактируйте код и запускайте его прямо здесь — интерпретатор работает в браузере, ничего устанавливать не нужно. За каждый запуск — +2 XP.

Вывод

Запустите код, чтобы увидеть вывод.

Коротко: что запомнить

  • var — явное объявление с типом; на уровне пакета работает только он.
  • Каждая переменная получает нулевое значение своего типа — мусора в памяти не бывает.
  • Внутри функций пишут :=: тип выводится сам, но слева нужен хотя бы один новый идентификатор.
  • Неиспользуемая переменная — ошибка компиляции, а не предупреждение.
  • const — значение на этапе компиляции; константу нельзя переприсвоить.
  • iota — автосчётчик внутри блока const: идеален для перечислений и размеров памяти.
  • Область видимости ограничена блоком; заглавная буква имени означает экспорт из пакета.

Домашнее задание

Напишите программу, которая объявляет на уровне пакета константу секундВМинуте = 60 и три переменные с нулевыми значениями (int, string, bool). В main присвойте им осмысленные значения (например, часы = 2, имя = "Алёна", isDone = true), а затем выведите их через fmt.Println. Добавьте блок const с тремя днями недели через iota и выведите их номера.

Личные заметкиПусто
Хранятся только в вашем браузере (Go Base · Переменные и константы)

Вопросы и ответы по уроку

Чем := отличается от var и когда что использовать?

Оба объявляют переменные, но := короче и выводит тип сам, работает только внутри функций. var нужен на уровне пакета, при явном указании типа и в var-блоках. Внутри функций большинство Го-разработчиков пишут :=, оставляя var для случаев, когда тип важнее значения.

Что будет, если присвоить значение константе?

Программа не скомпилируется: Go выдаст ошибку «cannot assign to». Это проверка на этапе компиляции, а не во время работы — константа гарантирует неизменность сильнее, чем, например, const в JavaScript.

Почему нельзя объявить неиспользуемую переменную?

Go рассматривает неиспользуемую переменную как признак ошибки в логике и отказывается собирать программу. Это философия языка: ошибки должны ловиться как можно раньше. Временную переменную можно «проглотить» пустым идентификатором _.

Термины урока

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

Следующая ступень учебного пути

Go Pro25 уроков~14 чпрактика25 уроков уже доступны

Продвинутые возможности Go: от замыканий до рефлексии

Продвинутый курс Go: замыкания, defer, горутины, каналы, контекст, интерфейсы, дженерики, рефлексия, тесты и JSON. 25 уроков с практикой в браузере.

  • Писать и читать конкурентный код: go-функции, каналы, select
  • Управлять жизненным циклом операций через context
  • Проектировать API на интерфейсах
  • Использовать дженерики без злоупотребления