---
title: "Переменные и константы"
description: "Переменные в Go: var, короткое объявление :=, константы const и iota. Разбираем нулевые значения, область видимости и типичные ошибки — с примерами."
course: "Go с нуля: базовый синтаксис и конструкции"
url: "https://golangmaster.ru/courses/go-basics/lessons/04-peremennye-konstanty/"
published: "2026-09-19"
updated: "2026-09-28"
minutes: 20
---

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

*Курс «Go с нуля: базовый синтаксис и конструкции», урок 4 из 25. ~20 минут. Обновлено 28.09.2026. Источник: https://golangmaster.ru/courses/go-basics/lessons/04-peremennye-konstanty/*

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

### Переменные в 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 и другие целые | 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` на уровне пакета (вне всех функций)?

- Всё скомпилируется: := можно использовать где угодно
- ✅ Ошибка компиляции: короткое объявление := разрешено только внутри функций
- n получит нулевое значение 0
- Компилятор выдаст предупреждение, но соберёт программу

На уровне пакета допустимы только var и const. := — сокращение для локальных объявлений внутри функций. Для пакета пишите var n = 10 или var n int = 10.

### Что выведет код?

```go
package main

import "fmt"

func main() {
        x := 5
        y := x
        x = 10
        fmt.Println(x, y)
}
```

**Что выведет программа?**

- 10 10
- ✅ 10 5
- 5 10
- ошибка компиляции

y := x копирует значение x в момент присваивания. После этого x и y — независимые переменные: изменение x не влияет на y. В Go нет ссылочной семантики для чисел, только для указателей и ссылочных типов вроде срезов.

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

> **Константу нельзя переприсвоить.** Строка `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. Затем попробуйте вставить в начало первого блока строку `_` (пустой идентификатор) и посмотрите, как сдвинутся все значения.

```go
package main

import "fmt"

const (
        Mon = iota
        Tue
        Wed
        Thu
        Fri
)

const (
        Second = 1
        Minute = 60 * Second
        Hour   = 60 * Minute
)

func main() {
        fmt.Println("Дни:", Mon, Tue, Wed, Thu, Fri)
        fmt.Println("Время:", Second, Minute, Hour)
}
```

### Область видимости: где живёт переменная

Объявленная переменная доступна не везде, а внутри своей области видимости — блока кода, в котором она объявлена. Переменная уровня пакета видна во всём пакете, объявленная внутри функции — только в этой функции, объявленная внутри цикла 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` — и через полгода тоже.

### Соберите код: константа и переменная

Впишите пропущенные ключевые слова и тип так, чтобы программа скомпилировалась и напечатала длину окружности.

```go
package main

import "fmt"

___ Pi float64 = 3.14159

func main() {
        ___ radius = 2.5
        length := 2 * Pi * radius
        fmt.Println("Длина:", length)
}
```

Подсказка: Pi не меняется — это константа. radius объявлен на уровне функции без типа — здесь уместно короткое объявление или var.

Правильные ответы в пропусках: `const`, `var`

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

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

Правильный порядок строк:

1. `package main`
2. `import "fmt"`
3. `const Status = "ready"`
4. `func main() {`
5. `fmt.Println(Status)`
6. `}`

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

- **var x int без значения** — x получает нулевое значение типа: 0 для чисел, "" для строк, false для bool, nil для ссылочных типов.
- **Разница между := и =** — := объявляет новую переменную (только внутри функций, слева нужен хотя бы один новый идентификатор). = просто присваивает значение существующей.
- **const Pi = 3.14** — Значение вычисляется на этапе компиляции, изменять нельзя. Нетипизированная константа подстраивается под контекст.
- **iota** — Счётчик внутри блока const: 0, 1, 2... на каждой строке. Сбрасывается в новом блоке. Часто комбинируют с битовыми операциями вроде 1 << (10 * iota).
- **Zero value строки** — Пустая строка "" — не nil и не «мусор». Поэтому len(s) у необъявленной строки корректно даст 0.
- **Имя с заглавной буквы** — MaxUsers экспортируется из пакета (виден другим пакетам), maxUsers — приватный. Это работает и для констант, и для функций, и для типов.

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

- 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 рассматривает неиспользуемую переменную как признак ошибки в логике и отказывается собирать программу. Это философия языка: ошибки должны ловиться как можно раньше. Временную переменную можно «проглотить» пустым идентификатором _.
