Лекция 2. Переменные, типы и управляющие конструкции¶
Объявление переменных¶
В Go три способа объявить переменную:
// 1. Полная форма с явным типом
var x int = 42
// 2. С выводом типа (тип компилятор берёт из правой части)
var y = 42 // тип int
// 3. Короткое объявление (только внутри функций)
z := 42 // тип int
Оператор := — комбинация объявления и инициализации. Самая частая форма. Работает только внутри функций (на уровне пакета — нельзя, там только var/const).
Несколько переменных сразу¶
var a, b int = 1, 2
x, y := 3.14, "hello"
// Блок объявлений
var (
name string
age int
active bool
)
Zero values¶
Если переменная объявлена без инициализации, она получает нулевое значение соответствующего типа:
| Тип | Zero value |
|---|---|
| числовые | 0 |
bool |
false |
string |
"" (пустая) |
| указатели, срезы, карты, каналы, функции, интерфейсы | nil |
Это важное отличие от Python: переменная всегда инициализирована, обращение к ней не упадёт с ошибкой «не определена». Но если вы забыли её присвоить — получите 0 или "", и баг будет тихим.
Неиспользуемые переменные — это ошибка компиляции¶
Компилятор не даст собрать. Это сделано намеренно: лишние переменные — частый источник багов. Если переменная нужна, но временно не используется, присвойте её в _:
То же правило для импортов: неиспользуемый импорт — ошибка компиляции.
Константы¶
Константы вычисляются во время компиляции. Тип можно не указывать — он будет «нетипизированной константой» и приведётся к нужному типу в момент использования.
iota — генератор последовательностей¶
const (
Sunday = iota // 0
Monday // 1
Tuesday // 2
Wednesday // 3
Thursday // 4
Friday // 5
Saturday // 6
)
iota начинается с 0 на каждом const-блоке и инкрементируется на каждой следующей строке. Удобно для enum-подобных конструкций (полноценных enum в Go нет).
Базовые типы¶
Целочисленные¶
| Тип | Размер | Диапазон |
|---|---|---|
int8 |
1 байт | -128..127 |
int16 |
2 байта | -32768..32767 |
int32 |
4 байта | -2³¹..2³¹−1 |
int64 |
8 байт | -2⁶³..2⁶³−1 |
int |
4 или 8 байт (платформо-зависимо) | как int32/int64 |
uint8 (= byte) |
1 байт | 0..255 |
uint16, uint32, uint64, uint |
— | беззнаковые аналоги |
uintptr |
размер указателя | для unsafe-кода |
В отличие от Python (где int — произвольной точности), в Go размер фиксирован. Это даёт скорость и предсказуемое потребление памяти, но при переполнении получаете wrap-around: var x int8 = 127; x++ // -128. Никаких ошибок — компилятор молчит.
Числа с плавающей точкой¶
float32— IEEE-754 single precision;float64— IEEE-754 double precision (это «нормальный» float, используется по умолчанию).
Комплексные¶
complex64 и complex128 — комплексные числа. Редко нужны, упоминаем для полноты.
Логический¶
Никаких неявных преобразований из числа в bool (как в Python, где 0 == False). if 1 { ... } — ошибка компиляции.
Строки и руны¶
s := "Привет, мир!"
fmt.Println(len(s)) // 22 — длина в БАЙТАХ, не в символах
// Перебор по рунам (Unicode code points)
for i, r := range s {
fmt.Printf("%d: %c (%U)\n", i, r, r)
}
string в Go — это неизменяемая последовательность байт (UTF-8 по соглашению). byte — это псевдоним uint8. rune — псевдоним int32, представляет одну Unicode code point.
s := "Привет"
fmt.Println(len(s)) // 12 (каждая кириллическая буква = 2 байта в UTF-8)
fmt.Println(utf8.RuneCountInString(s)) // 6
Подробно про кодировки и байты — в лекции 4 темы 3.
Преобразование типов¶
Никаких неявных приведений. Всё пишется явно:
Это раздражает поначалу, но спасает от множества тонких багов. Сравните с Python, где 1 + 1.0 == 2.0 работает молча — в Go это int + float64 и не скомпилируется.
Числа в строки и обратно¶
Через пакет strconv:
import "strconv"
s := strconv.Itoa(42) // "42"
n, err := strconv.Atoi("42") // 42, nil
f, err := strconv.ParseFloat("3.14", 64)
str := strconv.FormatFloat(3.14, 'f', 2, 64) // "3.14"
Любой strconv.Parse* возвращает второе значение error — это обязательная конвенция Go (см. лекцию 3).
if с инициализацией¶
Базовый синтаксис без скобок вокруг условия:
if x > 10 {
fmt.Println("big")
} else if x > 0 {
fmt.Println("small")
} else {
fmt.Println("non-positive")
}
Уникальная фича Go — короткая инициализация прямо в условии:
if n, err := strconv.Atoi("42"); err == nil {
fmt.Println("got number:", n)
} else {
fmt.Println("error:", err)
}
// здесь n и err уже не видны — их scope ограничен if/else
Это идиоматичный паттерн. Переменные живут только внутри блока if/else, не загрязняют внешний scope.
Тернарного оператора нет¶
В Go сознательно отказались от ?:. Пишите полноценный if или используйте функции-обёртки. С Go 1.18 (дженерики) можно написать свою:
Но идиоматично — обычный if.
for — единственный цикл¶
В Go нет while, нет do-while. Есть только for, который умеет всё.
// классический for
for i := 0; i < 10; i++ {
fmt.Println(i)
}
// while-эквивалент
for x < 100 {
x *= 2
}
// бесконечный цикл
for {
if done {
break
}
}
// for-range по коллекции
nums := []int{10, 20, 30}
for i, v := range nums {
fmt.Println(i, v)
}
// только индекс
for i := range nums {
fmt.Println(i)
}
// только значение (индекс игнорируем через _)
for _, v := range nums {
fmt.Println(v)
}
range работает по срезам, массивам, строкам (выдаёт руны!), картам и каналам.
Метки и break из вложенного цикла¶
Аналог continue тоже работает с метками. Используйте редко — чаще лучше вынести в функцию и сделать return.
switch¶
switch в Go мощнее, чем в C/Python.
switch day {
case "monday", "tuesday", "wednesday", "thursday", "friday":
fmt.Println("будний")
case "saturday", "sunday":
fmt.Println("выходной")
default:
fmt.Println("неизвестно")
}
Особенности:
- Нет неявного
fallthrough— каждыйcaseсам по себе. Это противоположность C, где забытыйbreak— частый баг. - Можно перечислять несколько значений через запятую.
- Можно использовать выражения вместо тегов:
switch {
case x < 0:
fmt.Println("отрицательное")
case x == 0:
fmt.Println("ноль")
case x > 0:
fmt.Println("положительное")
}
Это эквивалент длинной цепочки if/else if/else.
fallthroughдоступен явно, если очень нужен:
- Type switch — особый вид, разбирается в лекции 5 (методы и интерфейсы).
defer — отложенный вызов¶
Уникальная конструкция Go. defer f() гарантирует, что функция f будет вызвана при выходе из текущей функции (нормальном или через panic).
func readFile(name string) error {
f, err := os.Open(name)
if err != nil {
return err
}
defer f.Close() // выполнится перед return, что бы дальше ни случилось
// ... работа с f ...
return nil
}
Это аналог Python with (context manager) или Java try-with-resources. Но удобнее: вы видите открытие и закрытие рядом, а сама работа может занимать любое количество строк.
Правила:
- Аргументы вычисляются в момент
defer, а не в момент вызова:
- Несколько
deferвыполняются в обратном порядке (LIFO):
deferвыполнится даже приpanic. Это основа механизмаrecover(лекция 3).
Типичные применения: закрытие файлов, разблокировка мьютексов, восстановление состояния (debug-логи).
Указатели¶
В Go есть указатели, но без арифметики (как в C). Это просто адрес переменной.
x := 42
p := &x // p — указатель на x (тип *int)
fmt.Println(*p) // 42 — разыменование
*p = 100
fmt.Println(x) // 100
Зачем указатели нужны:
- Передать большую структуру в функцию без копирования.
- Дать функции возможность изменять переданный аргумент.
- Различать «значение есть, оно нулевое» и «значения нет» (через
*int = nil).
Указатели в Go безопасны: нельзя получить адрес «куда попало», нельзя сделать p++. Сборщик мусора отслеживает их и не освобождает память, пока есть живые ссылки.
Параллель с Python¶
| Python | Go |
|---|---|
x = 42 |
x := 42 |
x: int = 42 |
var x int = 42 |
PI = 3.14 |
const Pi = 3.14 |
| динамическая типизация | статическая, явные преобразования |
int — bignum |
int фиксированного размера, переполнения молчат |
if x: |
if x != 0 { ... } (не приводит число к bool) |
if x > 0: ... elif y > 0: ... else: |
if x > 0 { ... } else if y > 0 { ... } else { ... } |
x if cond else y |
только полноценный if |
while, for ... in ... |
только for (в разных формах) |
match (3.10+) |
switch (без fallthrough) |
with open(...) as f: |
defer f.Close() |
| ссылки на объекты везде | значения + указатели &x / *p |
Итог¶
В Go явная типизация, фиксированные размеры чисел, нет неявных преобразований. Единственный цикл — for, мощный switch без fallthrough, уникальный defer для отложенной очистки. Указатели есть, но без арифметики и под GC. В следующей лекции — функции, ошибки как значения и panic/recover.