Модуль
1Hello, World!2Переменные в Go← вы здесь3Целые числа4Строки и руны5float64 и числа с плавающей точкой6if, switch, for7bool и логические операторы8Константы и iota9Пакет fmt: форматирование вывода
Урок 2~15 минут

Переменные в Go

Переменные — три закона Go

В Go три правила, которые сразу отличают его от других языков:

1. Объявил — используй. Компилятор не соберёт программу с неиспользуемой переменной. Это не предупреждение — это ошибка:

declared and not used
Ctrl+Enter
1

Это кажется раздражающим поначалу. Но это дисциплина: в Go-коде нет мусорных переменных, которые «может пригодятся».

2. Тип выводится автоматически. Не нужно писать int или string — Go смотрит на значение и сам решает:

go
name := "Alice"   // Go знает: это string
count := 42       // Go знает: это int
pi := 3.14        // Go знает: это float64

3. Нулевые значения. Каждая переменная без явного значения получает «ноль» для своего типа. Нет undefined, нет null для базовых типов.


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

var — явный способ

go
var name string = "Alice"
var age  int    = 30
var pi   float64 = 3.14
var ok   bool   = true

Многословно, но иногда нужно — особенно на уровне пакета (вне функций). := там не работает.

Можно опустить тип — Go выведет сам:

go
var name = "Alice"   // тип: string
var age  = 30        // тип: int

Можно опустить значение — переменная получит нулевое значение:

go
var name string  // ""
var count int    // 0
var flag bool    // false

:= — короткое объявление

go
name := "Alice"
age  := 30

Это самый частый способ. Правила:

  • Работает только внутри функций
  • Хотя бы одна переменная слева должна быть новой
  • Компилятор сам выводит тип
go
func main() {
    x := 10       // новая переменная x
    x, y := 20, 5 // x переиспользуется, y — новая
    // x теперь 20, y = 5
}

Нулевые значения

Это одна из сильных сторон Go. Ни одна переменная не бывает «пустой» в плохом смысле:

ТипНулевое значение
int, int64, uint, ...0
float32, float640.0
string"" (пустая строка)
boolfalse
pointer, slice, map, chan, funcnil
нулевые значения
Ctrl+Enter
1

Это означает: если ты объявил структуру с полями — все поля уже инициализированы. Нет NullPointerException при обращении к полям struct.


Множественное объявление

Несколько переменных одной строкой:

go
a, b := 10, 20
x, y, z := "hello", true, 3.14

Групповое объявление через var — удобно на уровне пакета:

go
var (
    host  = "localhost"
    port  = 8080
    debug = false
)

Классический своп без временной переменной:

go
a, b := 1, 2
a, b = b, a  // теперь a=2, b=1

Это не магия — Go вычисляет правую часть целиком, потом присваивает. В других языках для этого нужна temp.


Область видимости (scope)

Переменная живёт в том блоке {}, где объявлена. Вышел из блока — переменная уничтожена:

go
func main() {
    x := 10       // x видна в main
 
    if true {
        y := 20   // y видна только здесь
        fmt.Println(x, y)  // OK
    }
 
    fmt.Println(x)  // OK
    fmt.Println(y)  // ОШИБКА: undefined: y
}

Shadowing — переменная прячет другую

Это одна из самых коварных особенностей Go. Если внутри вложенного блока объявить переменную с тем же именем — она не изменит внешнюю, а создаст новую, которая закрывает (shadows) старую.

go
var x = 1  // пакетная переменная
 
func main() {
    x := 2  // новая x в main, пакетная x теперь недоступна
 
    if true {
        x := 3  // ещё одна новая x, x=2 теперь недоступна
        fmt.Println(x)  // 3
    }
 
    fmt.Println(x)  // 2 — вернулась x из main
}
 
fmt.Println(x)  // здесь x = 1 — пакетная

Интерактивный визуализатор выше показывает, как переменные накапливаются в «стеке» при входе в блоки и исчезают при выходе.

shadowing
Ctrl+Enter
1

Почему shadowing опасен

go
err := doSomething()
 
if err != nil {
    err := fmt.Errorf("wrapped: %w", err)  // ← ОШИБКА ДИЗАЙНА
    // это новая err, не та что снаружи!
    log.Fatal(err)
}
 
// здесь err — старая, необёрнутая

Используй = вместо := если хочешь изменить существующую переменную, а не создать новую.


Пустой идентификатор _

Go требует использовать все объявленные переменные. Но иногда нужно «выбросить» значение. Для этого есть специальный идентификатор _:

go
// Функция возвращает два значения
file, err := os.Open("data.txt")

Если ошибка не нужна (хотя так делать не стоит):

go
file, _ := os.Open("data.txt")  // игнорируем err

Нормальные случаи для _:

go
// Только значения из range, индекс не нужен
for _, v := range numbers {
    fmt.Println(v)
}
 
// Импорт только для side effects (запускает init())
import _ "net/http/pprof"
 
// Деструктуризация, когда нужна только часть
x, _, z := threeValues()

_ — это не переменная. Ты не можешь прочитать из неё значение. Это буквально «мусорная корзина».


Проверка типа переменной

Если хочешь узнать какой тип вывел Go — используй fmt.Printf с форматом %T:

go
x := 42
y := 3.14
z := "hello"
 
fmt.Printf("%T\n", x)  // int
fmt.Printf("%T\n", y)  // float64
fmt.Printf("%T\n", z)  // string

%T — тип переменной. %v — значение в дефолтном формате. Эти два формата ты будешь использовать постоянно при отладке.


Частые ошибки новичков

1. := на уровне пакета:

go
package main
 
x := 10  // СИНТАКСИЧЕСКАЯ ОШИБКА
var x = 10  // OK

2. Shadowing при обработке ошибок:

go
result, err := step1()
if err == nil {
    result, err := step2()  // ← создаёт НОВЫЕ переменные!
    // внешние result и err не изменены
}

3. Объявить и не использовать:

go
func main() {
    count := 0
    // используй count или удали
    fmt.Println("done")  // ОШИБКА: count declared and not used
}

В следующем уроке разберём целочисленные типы Go — int, int8/16/32/64, uint, и почему важно понимать переполнение.

Объявил переменную — используй. Компилятор Go не даст тебе забыть: declared and not used — это ошибка, не предупреждение.
Способы объявления переменных
💡 Явно, многословно. Используют на уровне пакета.
Shadowing — визуализация
Стек переменных
x = 1ACTIVE
var x = 1
Переменная x = 1 объявлена на уровне пакета. Видна везде.
Пустой идентификатор _
file, _ := os.Open("data.txt")
// ошибка проигнорирована — опасно
⚠️ Технически работает, но если файл не существует — программа упадёт позже.
🎯
Миссия 1 из 5
Какой оператор используется для короткого объявления переменной внутри функции?