Условия: if / else / else if / switch
Учим программу принимать решения: проверяем условия, выбираем ветки выполнения и строим логику
В предыдущем уроке мы разобрались с операторами: научились складывать, сравнивать и комбинировать значения. Но до сих пор наши программы выполнялись строго сверху вниз, одна строка за другой, без каких-либо разветвлений. В реальной жизни так не бывает. Когда вы утром смотрите в окно и видите дождь, вы берёте зонт. Если солнечно, одеваетесь легче. Это и есть условие: если что-то истинно, делай одно; иначе делай другое. Сегодня мы научим нашу программу принимать решения с помощью конструкций if, else, else if и switch.
🔀 Базовый if — простейшее условие
Конструкция if — это самая базовая форма ветвления. Она говорит: «Если условие истинно, выполни код внутри фигурных скобок». Вот как это выглядит:
package main
import "fmt"
func main() {
temperature := 35
if temperature > 30 {
fmt.Println("На улице жарко!")
}
}
Разберём по частям. Переменная temperature хранит значение 35. Затем мы пишем if temperature > 30 — это условие. Если оно истинно (а 35 действительно больше 30), выполняется код внутри фигурных скобок: выводится строка «На улице жарко!». Если бы temperature была, скажем, 20, программа просто пропустила бы этот блок и ничего не напечатала.
Обратите внимание на важную особенность Go:
💡 Подсказка: в Go условие после if пишется без круглых скобок. Это отличает Go от многих других языков. Пишем if x > 10, а не if (x > 10). Если вы добавите скобки, программа скомпилируется, но это считается плохим стилем, и большинство Go-инструментов автоматически их уберут.
Ещё одна важная деталь: открывающая фигурная скобка { обязательно должна стоять на той же строке, что и if. Нельзя перенести её на следующую строку — Go этого не позволяет. Это не рекомендация, а требование языка.
// ТАК ПРАВИЛЬНО:
if temperature > 30 {
fmt.Println("Жарко!")
}
// ТАК НЕПРАВИЛЬНО — ошибка компиляции:
// if temperature > 30
// {
// fmt.Println("Жарко!")
// }
Фигурные скобки обязательны даже для одной строки внутри if. В отличие от некоторых языков, где можно написать if x > 0 doSomething() без скобок, в Go это невозможно. Это делает код более читаемым и предсказуемым.
↔️ if / else — два пути
Часто нужно не просто выполнить действие, если условие истинно, но и сделать что-то другое, если условие ложно. Для этого используется else:
package main
import "fmt"
func main() {
age := 16
if age >= 18 {
fmt.Println("Вы совершеннолетний")
} else {
fmt.Println("Вы несовершеннолетний")
}
}
Здесь age равен 16. Условие age >= 18 ложно, поэтому программа пропускает первый блок и выполняет блок else. На экране появится: «Вы несовершеннолетний».
Обратите внимание: else пишется на той же строке, что и закрывающая скобка предыдущего блока }. Это обязательно:
// ПРАВИЛЬНО — else на той же строке, что и }
if age >= 18 {
fmt.Println("Совершеннолетний")
} else {
fmt.Println("Несовершеннолетний")
}
// НЕПРАВИЛЬНО — ошибка компиляции:
// if age >= 18 {
// fmt.Println("Совершеннолетний")
// }
// else {
// fmt.Println("Несовершеннолетний")
// }
Рассмотрим ещё один пример — проверка чётности числа:
package main
import "fmt"
func main() {
number := 7
if number%2 == 0 {
fmt.Println(number, "— чётное число")
} else {
fmt.Println(number, "— нечётное число")
}
}
Оператор % (остаток от деления) мы изучали в прошлом уроке. Если остаток от деления на 2 равен нулю, число чётное. Иначе — нечётное. Программа выведет: «7 — нечётное число».
🔗 else if — цепочка условий
Иногда два варианта — «да» и «нет» — недостаточно. Например, вы хотите оценить результат экзамена: отлично, хорошо, удовлетворительно или неудовлетворительно. Для этого используется else if — промежуточные условия между if и else:
package main
import "fmt"
func main() {
score := 82
if score >= 90 {
fmt.Println("Отлично!")
} else if score >= 70 {
fmt.Println("Хорошо")
} else if score >= 50 {
fmt.Println("Удовлетворительно")
} else {
fmt.Println("Неудовлетворительно")
}
}
Давайте проследим, как программа думает. Переменная score равна 82.
- Проверяем:
82 >= 90? Нет. Переходим дальше. - Проверяем:
82 >= 70? Да! Выполняем этот блок — выводим «Хорошо». - Все остальные ветки пропускаются.
Это важный момент: выполняется только первый подходящий блок. Хотя 82 также больше 50, программа не дойдёт до этой проверки, потому что уже нашла подходящее условие выше. Условия проверяются сверху вниз, и как только одно оказывается истинным, остальные игнорируются.
Блоков else if может быть сколько угодно. Финальный else необязателен, но полезен как «ловушка» для всех оставшихся случаев — на случай, если ни одно условие не подошло.
⚡ Короткий if с инициализацией
В Go есть уникальная возможность, которой нет во многих других языках: вы можете объявить переменную прямо внутри if перед условием. Это называется if с инициализацией (short statement). Выглядит это так:
package main
import "fmt"
func main() {
if x := 10; x > 5 {
fmt.Println("x больше 5, а именно:", x)
}
}
Обратите внимание на точку с запятой ;. Она разделяет две части: сначала идёт инициализация (x := 10), потом условие (x > 5). Это позволяет объявить переменную и сразу проверить её значение в одной строке.
Зачем это нужно? Переменная x, объявленная таким образом, существует только внутри блока if (и связанных с ним else if / else). За пределами конструкции она недоступна. Это позволяет не засорять внешнюю область видимости временными переменными.
package main
import "fmt"
func main() {
if length := len("Привет"); length > 3 {
fmt.Println("Строка длинная:", length, "символов")
} else {
fmt.Println("Строка короткая:", length, "символов")
}
// fmt.Println(length) // ОШИБКА! Переменная length здесь не существует
}
В этом примере мы вычисляем длину строки и сразу проверяем, больше ли она 3. Переменная length доступна и в блоке if, и в блоке else, но после закрывающей скобки она перестаёт существовать.
💡 Подсказка: короткий if с инициализацией — это идиоматичный Go. Вы будете очень часто встречать его в реальном коде, особенно при обработке ошибок: if err := doSomething(); err != nil { ... }. Пока не обязательно запоминать этот паттерн — мы подробно разберём ошибки в будущих уроках. Главное, чтобы вы понимали механизм.
🪆 Вложенные условия
Условия можно вкладывать друг в друга. Например, сначала проверяем, есть ли у человека билет, а потом — какого он класса:
package main
import "fmt"
func main() {
hasTicket := true
class := "бизнес"
if hasTicket {
if class == "бизнес" {
fmt.Println("Добро пожаловать в бизнес-класс!")
} else {
fmt.Println("Добро пожаловать в эконом-класс!")
}
} else {
fmt.Println("Пожалуйста, приобретите билет")
}
}
Программа сначала проверяет, есть ли билет (hasTicket — это булева переменная, поэтому можно писать просто if hasTicket, без == true). Если билет есть, внутри проверяется класс. Если билета нет, программа сразу переходит к else внешнего условия.
💡 Подсказка: вложенные условия работают, но старайтесь не злоупотреблять глубиной вложенности. Код с тремя-четырьмя уровнями вложенных if тяжело читать. Часто вложенные условия можно заменить цепочкой else if или логическими операторами && и ||.
🔄 switch — элегантная замена длинным if-else
Когда у вас много вариантов для одной переменной, цепочка else if становится громоздкой. Представьте, что вы проверяете день недели. С if / else if это выглядит так:
// Громоздко и трудно читать:
if day == "понедельник" {
fmt.Println("Начало недели")
} else if day == "вторник" {
fmt.Println("Второй день")
} else if day == "среда" {
fmt.Println("Середина недели")
} else if day == "четверг" {
fmt.Println("Почти пятница")
} else if day == "пятница" {
fmt.Println("Ура, пятница!")
} else {
fmt.Println("Выходной!")
}
Конструкция switch решает эту проблему — она делает код чистым и читаемым:
package main
import "fmt"
func main() {
day := "среда"
switch day {
case "понедельник":
fmt.Println("Начало недели")
case "вторник":
fmt.Println("Второй день")
case "среда":
fmt.Println("Середина недели")
case "четверг":
fmt.Println("Почти пятница")
case "пятница":
fmt.Println("Ура, пятница!")
default:
fmt.Println("Выходной!")
}
}
Как это работает: после слова switch указывается выражение, которое мы проверяем (day). Затем идут блоки case с возможными значениями. Go сравнивает day с каждым case сверху вниз. Когда находит совпадение, выполняет код этого блока. Блок default выполняется, если ни один case не подошёл (как else в конструкции if).
Ключевое отличие Go: автоматический break
Если вы знакомы с другими языками (C, Java, JavaScript), то знаете, что в switch нужно писать break после каждого case, иначе выполнение «провалится» вниз в следующий блок. В Go break происходит автоматически! Каждый case сам по себе завершается после выполнения. Это избавляет от целого класса ошибок, связанных с забытым break.
📦 Несколько значений в одном case
Иногда несколько значений должны обрабатываться одинаково. В Go можно перечислить их через запятую:
package main
import "fmt"
func main() {
day := "суббота"
switch day {
case "понедельник", "вторник", "среда", "четверг", "пятница":
fmt.Println("Рабочий день")
case "суббота", "воскресенье":
fmt.Println("Выходной!")
default:
fmt.Println("Неизвестный день")
}
}
Здесь все пять рабочих дней объединены в один case, и оба выходных — в другой. Это гораздо удобнее, чем писать пять отдельных блоков с одинаковым кодом. Программа выведет: «Выходной!».
🧩 switch без условия — чистая замена if/else
В Go есть особая форма switch — без выражения после ключевого слова. Такой switch эквивалентен switch true: он проверяет каждый case как самостоятельное условие. Это просто более чистый способ записать цепочку if / else if:
package main
import "fmt"
func main() {
temperature := 25
switch {
case temperature < 0:
fmt.Println("Мороз")
case temperature < 10:
fmt.Println("Холодно")
case temperature < 20:
fmt.Println("Прохладно")
case temperature < 30:
fmt.Println("Тепло")
default:
fmt.Println("Жарко!")
}
}
Заметьте: после switch нет никакой переменной. Каждый case содержит собственное условие. Go проверяет их по порядку и выполняет первый истинный. Переменная temperature равна 25, поэтому temperature < 30 — первое истинное условие. Вывод: «Тепло».
Такой switch — любимый приём многих Go-разработчиков. Он читается проще, чем длинная цепочка if / else if / else if / else, и выглядит аккуратнее.
⬇️ fallthrough — принудительное проваливание
Мы говорили, что в Go каждый case автоматически завершается. Но иногда вам нужно, чтобы после одного case выполнился и следующий. Для этого существует ключевое слово fallthrough:
package main
import "fmt"
func main() {
level := 1
switch level {
case 1:
fmt.Println("Доступ: чтение")
fallthrough
case 2:
fmt.Println("Доступ: запись")
fallthrough
case 3:
fmt.Println("Доступ: администрирование")
}
}
Вывод программы:
Доступ: чтение
Доступ: запись
Доступ: администрирование
Несмотря на то что level равен 1, благодаря fallthrough выполнение «проваливается» в каждый следующий case. Это полезно в редких случаях, когда уровни доступа накапливаются: уровень 1 даёт все три права, уровень 2 — только запись и администрирование, уровень 3 — только администрирование.
💡 Подсказка: fallthrough используется в Go довольно редко. Он безусловный: переход в следующий case произойдёт всегда, без проверки условия. Поэтому используйте его только тогда, когда действительно нужно «провалиться» в следующий блок. В большинстве ситуаций перечисление через запятую (case "a", "b":) — более удобное решение.
🔧 switch с инициализацией
Как и if, конструкция switch поддерживает короткую инициализацию перед проверяемым выражением:
package main
import (
"fmt"
"time"
)
func main() {
switch hour := time.Now().Hour(); {
case hour < 6:
fmt.Println("Ночь")
case hour < 12:
fmt.Println("Утро")
case hour < 18:
fmt.Println("День")
default:
fmt.Println("Вечер")
}
}
Здесь hour получает текущий час, и мы сразу проверяем его в switch. Переменная hour существует только внутри конструкции switch, точно так же, как переменная в if с инициализацией. Функция time.Now().Hour() возвращает текущий час (от 0 до 23) — мы познакомимся с пакетом time подробнее позже, а пока просто запомните, что в Go можно легко получить текущее время.
🛠 Практика: определитель времени суток
Давайте соберём всё воедино и напишем программу, которая по заданному часу определяет время суток и выдаёт соответствующее приветствие. Мы используем и if, и switch, чтобы вы увидели оба подхода в деле.
Вариант 1: через if / else if / else
package main
import "fmt"
func main() {
hour := 14 // Попробуйте менять это значение
fmt.Println("Сейчас", hour, "часов.")
if hour < 0 || hour > 23 {
fmt.Println("Ошибка: час должен быть от 0 до 23")
} else if hour < 6 {
fmt.Println("Доброй ночи! Пора спать 🌙")
} else if hour < 12 {
fmt.Println("Доброе утро! Время для кофе ☕")
} else if hour < 18 {
fmt.Println("Добрый день! Продуктивного времени 💻")
} else {
fmt.Println("Добрый вечер! Время отдыхать 🌆")
}
}
Обратите внимание: мы сначала проверяем, что час корректный (от 0 до 23). Это хорошая практика — всегда проверять входные данные. Оператор || означает «или»: если час меньше 0 или больше 23, выводится ошибка.
Вариант 2: через switch без условия
package main
import "fmt"
func main() {
hour := 14
fmt.Println("Сейчас", hour, "часов.")
switch {
case hour < 0 || hour > 23:
fmt.Println("Ошибка: час должен быть от 0 до 23")
case hour < 6:
fmt.Println("Доброй ночи! Пора спать 🌙")
case hour < 12:
fmt.Println("Доброе утро! Время для кофе ☕")
case hour < 18:
fmt.Println("Добрый день! Продуктивного времени 💻")
default:
fmt.Println("Добрый вечер! Время отдыхать 🌆")
}
}
Оба варианта делают одно и то же. Попробуйте поменять значение hour на разные числа — 3, 8, 15, 21, 25 — и посмотрите, что выведет программа. Запускайте так:
go run main.go
Вариант 3: используем реальное время
А теперь самое интересное — пусть программа сама определяет, сколько сейчас времени, и приветствует нас:
package main
import (
"fmt"
"time"
)
func main() {
// Получаем текущий час
hour := time.Now().Hour()
fmt.Printf("Сейчас %d часов. ", hour)
switch {
case hour < 6:
fmt.Println("Доброй ночи!")
case hour < 12:
fmt.Println("Доброе утро!")
case hour < 18:
fmt.Println("Добрый день!")
default:
fmt.Println("Добрый вечер!")
}
// Бонус: определяем день недели
switch day := time.Now().Weekday(); day {
case time.Saturday, time.Sunday:
fmt.Println("Сегодня выходной — отдыхайте!")
default:
fmt.Println("Сегодня рабочий день — за работу!")
}
}
Эта программа использует пакет time, чтобы получить реальный текущий час и день недели. Каждый раз, когда вы её запустите, результат будет разным — в зависимости от того, когда именно вы это делаете. Обратите внимание на бонусную часть: switch с инициализацией и проверкой дня недели. Здесь time.Saturday и time.Sunday — это константы из пакета time.
Мы также впервые использовали fmt.Printf. Это функция форматированного вывода: %d — это подстановка для целого числа. То есть вместо %d подставится значение hour. О форматировании мы поговорим подробнее в одном из будущих уроков.
⚖️ Когда использовать if, а когда switch
Новички часто спрашивают: «А что лучше — if или switch?» Вот простое правило:
Используйте if, когда:
У вас одно-два условия. Или условие сложное, с несколькими логическими операторами. Или вам нужен if с инициализацией для обработки ошибок.
Используйте switch, когда:
У вас три и более вариантов для одной переменной. Или вы проверяете конкретные значения (строки, числа). Или хотите сделать код более читаемым и структурированным.
На практике обе конструкции взаимозаменяемы. Нет ничего, что можно сделать с switch и нельзя с if, и наоборот. Разница — в читаемости. Выбирайте то, что делает ваш код понятнее.
📋 Итоги урока
ifв Go пишется без круглых скобок вокруг условия, а фигурные скобки обязательныelseиelse ifпозволяют создавать ветвление с несколькими путями выполненияifс инициализацией (if x := ...; x > 0) объявляет переменную, видимую только внутри блокаswitch— чистая альтернатива длинным цепочкамelse if; каждыйcaseзавершается автоматически, безbreak- В одном
caseможно перечислить несколько значений через запятую switchбез условия проверяет каждыйcaseкак отдельное булево выражениеfallthroughпринудительно передаёт выполнение в следующийcase(используется редко)- Мы написали программу-определитель времени суток, используя и
if, иswitch
В следующем уроке мы изучим циклы — конструкцию for во всех её формах. В Go for — единственный цикл, но он универсален: заменяет и while, и do-while, и foreach из других языков. Вы научитесь повторять действия заданное количество раз, перебирать элементы коллекций и создавать бесконечные циклы. После этого урока у вас будут все базовые инструменты для управления потоком программы!