1. Вступ
Майже кожна програма в реальному житті постійно робить вибір. Якщо користувач увів неправильні дані — показати підказку. Якщо вік менший за 18 — не продавати квиток. Якщо сума покупки більша за 3000 — дати знижку. Без розгалужень програма перетворюється на магнітофон: він чесно прокручує один сценарій, поки навколо все змінюється.
У Go для розгалуження є кілька інструментів керування потоком виконання, і найпростіший із них — if. Без нього не вийде написати гнучкий код, який реагує на вхідні дані.
Базовий if: умова bool і обовʼязкові {}
Найпростіший if — це «якщо умова істинна, виконай блок». І звучить він майже як людська мова. Але в Go є один принцип: краще суворо й передбачувано, ніж із магією. Тому умова в if зобовʼязана бути bool, а фігурні дужки — це частина синтаксису, а не прикраса.
Базова форма:
if умова {
// код, який виконається, якщо умова == true
}
if у Go
Приклад (наш мініпроєкт «Каса кінотеатру»: визначаємо, чи повнолітній відвідувач):
package main
import "fmt"
func main() {
age := 20
if age >= 18 {
fmt.Println("Дорослий квиток") // Дорослий квиток
}
}
Тут age >= 18 — вираз, результат якого має тип bool. Саме такі вирази, тобто порівняння, ми й використовуємо в умовах.
Чому не можна if age { ... }
У багатьох мовах ненульове число вважається true. У Go так не можна: int — це int, bool — це bool. Тому конструкція нижче не скомпілюється:
// так не можна в Go
age := 20
if age {
...
}
Якщо ви хочете перевірити «не нуль», пишіть явно:
if age != 0 {
...
}
Це може здаватися занудством, але на практиці рятує від купи дивних багів: програма не буде вгадувати, що саме ви мали на увазі.
2. if/else і else if: дві гілки та ланцюжок перевірок
if/else: виконується рівно одна гілка
Дуже часто одного «якщо істинно — зроби» недостатньо. Потрібен варіант «інакше». У Go це робиться через else: якщо умова хибна (false), виконається альтернативний блок. Зручно думати так: if обирає одні з двох дверей, і ви точно пройдете рівно через одні.
Форма:
if умова {
// гілка true
} else {
// гілка false
}
if/else у Go
Продовжимо з «касою кінотеатру»:
package main
import "fmt"
func main() {
age := 16
if age >= 18 {
fmt.Println("Дорослий квиток")
} else {
fmt.Println("Дитячий квиток") // Дитячий квиток
}
}
Зверніть увагу на важливу дрібницю синтаксису: else має стояти в тому самому рядку, що й закривальна } від if‑гілки, тобто ось так: } else {. Якщо перенести else на новий рядок, можна отримати помилку компіляції через правила автоматичної вставки крапок з комою.
else if: ланцюжок перевірок згори вниз
Коли варіантів більше ніж два, починається класика: «якщо вік менший за 7 — безкоштовно, якщо менший за 18 — дитячий, інакше — дорослий». У Go для цього є else if. Це виглядає як драбина: перевірка йде згори вниз, і виконається перша відповідна гілка.
Форма:
if cond1 {
...
} else if cond2 {
...
} else {
...
}
Приклад (більш реалістична каса):
package main
import "fmt"
func main() {
age := 12
if age < 7 {
fmt.Println("Безкоштовний вхід") // Безкоштовний вхід
} else if age < 18 {
fmt.Println("Дитячий квиток") // Дитячий квиток
} else {
fmt.Println("Дорослий квиток")
}
}
Чому порядок умов важливий
Перевірки йдуть згори вниз. Якщо ви випадково поставите широку умову раніше за вузьку, вузька може стати недосяжною.
Наприклад, ось так — погано:
package main
import "fmt"
func main() {
age := 6
if age < 18 {
fmt.Println("Дитячий квиток") // Дитячий квиток (а хотілося «Безкоштовний вхід»)
} else if age < 7 {
fmt.Println("Безкоштовний вхід")
} else {
fmt.Println("Дорослий квиток")
}
}
Гілка age < 7 ніколи не спрацює, тому що всі значення < 7 уже потрапили під < 18. Це одна з найчастіших логічних помилок новачків: програма коректна для компілятора, але не для реальності.
3. Блоки {} і область видимості
Коли ви бачите { ... }, думайте не про «дужки для краси», а про межу маленького світу. Усередині блока можна оголошувати змінні, і вони житимуть лише там. Це називається областю видимості (scope). Вона потрібна, щоб код не перетворювався на звалище змінних, які оголосили про всяк випадок і забули.
Уявіть кухню: якщо ви дістали ніж, порізали салат і одразу прибрали — кухня залишається чистою. Якщо ви залишаєте всі ножі, каструлі й ополоники на столі назавжди, готувати стає страшно. Змінні — це такі самі предмети на столі.
Приклад: змінна живе тільки всередині if
package main
import "fmt"
func main() {
if x := 10; x%2 == 0 {
fmt.Println("x парний:", x) // x парний: 10
} else {
fmt.Println("x непарний:", x)
}
// fmt.Println(x) // так не можна: x не видно зовні if
}
Тут x := 10 оголошено в ініціалізаторі if (його ми розберемо детальніше трохи нижче), і x доступний усередині обох гілок if/else, але не зовні.
Невелика таблиця для орієнтира
| Де оголосили змінну | Де її можна використовувати |
|---|---|
| У main (поза блоками) | У всьому main після оголошення |
| Усередині блока { ... } | Тільки всередині цього блока |
| У if x := ...; cond { ... } | Усередині if і else (і тільки там) |
Ця механіка згодом стане вашим найкращим другом: ви зможете тримати тимчасові змінні в мінімально потрібній області, і код стане простіше читати.
4. if з ініціалізатором: підготували дані й перевірили
Іноді умову не можна перевірити з повітря — спочатку потрібно обчислити значення. Для цього Go дозволяє писати if у формі «ініціалізація; умова». Це дуже практично, тому що тимчасова змінна не виходить назовні й не захаращує main.
Форма:
if init; cond {
...
} else {
...
}
Приклад: читаємо вік як рядок і намагаємося розпарсити
Ми вже знаємо strconv.Atoi і знаємо, що там може бути помилка. Поки не заглиблюючись у філософію помилок, використаємо це просто як факт: іноді парсинг не вдається.
package main
import (
"fmt"
"strconv"
)
func main() {
ageStr := "21"
if age, err := strconv.Atoi(ageStr); err == nil {
fmt.Println("Вік:", age) // Вік: 21
} else {
fmt.Println("Некоректний вік") // (не спрацює для "21")
}
}
Зверніть увагу: змінні age і err існують тільки всередині if/else. Ззовні їх немає — і це добре, бо в нашому main не лишається слідів від проміжних кроків.
До речі, у Go перевірка виду if err != nil { ... } — настільки популярний патерн, що його зазвичай запамʼятовують одним із перших: «зробив операцію → перевірив помилку → пішов далі».
5. Вкладені if: нормально, доки це не піраміда
Вкладений if — це коли всередині гілки if є ще один if. Це законно, працює й інколи справді зручно. Але у вкладеності є неприємний побічний ефект: відступи ростуть, мозок починає тримати контекст, і в якийсь момент ви читаєте код як давнє пророцтво: «якщо це було істинно, і якщо ось це було істинно, і якщо зорі зійшлися…»
Сьогодні наша мета — навчитися робити вкладені if охайно й усвідомлено, без перетворення програми на лабіринт.
Приклад: «каса кінотеатру» і студентська знижка
Припустімо, правила такі: якщо відвідувач неповнолітній — дитячий квиток. Якщо дорослий — дорослий, але якщо він студент, то «дорослий зі знижкою».
Зробимо це без логічних операторів (їх ми розберемо окремо), просто через вкладеність:
package main
import "fmt"
func main() {
age := 20
isStudent := true
if age >= 18 {
if isStudent {
fmt.Println("Дорослий квиток зі знижкою") // Дорослий квиток зі знижкою
} else {
fmt.Println("Дорослий квиток")
}
} else {
fmt.Println("Дитячий квиток")
}
}
Цей код цілком читабельний: вкладеність лише на один рівень, і логіка зрозуміла.
Схема: як думає програма
Іноді корисно уявити if/else як розвилку доріг:
flowchart TD
A[Початок] --> B{age >= 18?}
B -- так --> C{isStudent?}
C -- так --> D[Дорослий зі знижкою]
C -- ні --> E[Дорослий без знижки]
B -- ні --> F[Дитячий квиток]
Якщо під час читання коду ви ловите себе на думці «я вже забув, у якій гілці ми», — це сигнал: або зменшити вкладеність, або перейменувати умови та змінні, або трохи пізніше в курсі виділити логіку у функції. Але поки ми працюємо лише в межах main, тому фокус — на охайній структурі.
6. Мінізбірка: каса з введенням користувача та else if
Зараз ми зберемо цілісний приклад, який використовує те, що ви вже знаєте: fmt.Scan, if/else if/else, блоки й охайні назви змінних. Це не домашнє завдання, а просто демонстрація того, як частини мови складаються в одну маленьку програму.
Правила такі:
- вік читаємо зі stdin як число,
- якщо вік менший за 0 — це помилка введення,
- якщо менший за 7 — безкоштовно,
- якщо менший за 18 — дитячий,
- інакше — дорослий.
package main
import "fmt"
func main() {
var age int
fmt.Scan(&age)
if age < 0 {
fmt.Println("Помилка: вік не може бути відʼємним")
} else if age < 7 {
fmt.Println("Безкоштовний вхід")
} else if age < 18 {
fmt.Println("Дитячий квиток")
} else {
fmt.Println("Дорослий квиток")
}
}
Тут немає нічого магічного: програма просто обирає одну гілку. І саме це робить її корисною — вона реагує на різні вхідні дані по-різному.
7. Типові помилки під час роботи з if/else
Помилки в if зазвичай не повʼязані зі складними алгоритмами, а виникають через дрібні синтаксичні та логічні промахи. І найпідступніше: частину таких помилок компілятор ловить одразу, і це добре, а частина перетворюється на неправильну поведінку програми, а це вже розслідування рівня «хто зʼїв печиво вночі». Нижче — найчастіші граблі, які трапляються майже в усіх.
Помилка №1: умова не bool.
Новачки часто намагаються написати if age { ... }, бо «в інших мовах так можна». У Go умова зобовʼязана бути булевим виразом. Потрібно явно порівнювати: if age != 0 або if age > 0. Це може здаватися занудством, але в довшій перспективі економить час: код читається однозначно, без здогадок.
Помилка №2: забули, що {} обовʼязкові.
У Go не можна писати if cond println("hi"). Навіть якщо всередині один рядок, блок обовʼязковий. Спочатку це дратує, потім ви звикаєте й раптом починаєте цінувати: візуально завжди видно межі гілки, і коли додаєте другий рядок, нічого не ламається.
Помилка №3: else з нового рядка.
Якщо написати так:
if cond {
...
}
else {
...
}
можна отримати помилку компіляції. У Go else прийнято писати як } else { в одному рядку. Це повʼязано з тим, як Go розставляє приховані крапки з комою, але вам достатньо запамʼятати практичне правило: закривальна дужка та else дружать в одному рядку.
Помилка №4: переплутали порядок умов у else if.
Коли ви пишете діапазони, спочатку мають іти більш вузькі або строгі умови, а потім більш загальні. Інакше загальне перехопить усі випадки раніше. Класична пастка: поставити age < 18 вище, ніж age < 7, і потім дивуватися, чому «безкоштовний вхід» ніколи не настає.
Помилка №5: змінну оголошено всередині блока, а ви намагаєтеся використати її зовні.
Це особливо часто трапляється з ініціалізатором if: if x := ...; cond { ... }. Усередині все працює, а потім рука тягнеться написати fmt.Println(x) після if. Компілятор скаже «не знаю такого x», і матиме рацію. Якщо значення потрібне після розгалуження — оголошуйте змінну до if, а всередині лише присвоюйте.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ