JavaRush /Курси /Go SELF /if/else у Go: синтаксис і блоки

if/else у Go: синтаксис і блоки

Go SELF
Рівень 3 , Лекція 2
Відкрита

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, а всередині лише присвоюйте.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ