JavaRush /Курси /Go SELF /Оголошення змінних: var, :=

Оголошення змінних: var, :=

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

1. Що таке змінна в Go: ім’я, тип і значення

Коли ви тільки починаєте програмувати, легко уявляти змінну як «коробочку для числа». Це непоганий старт, але в Go у цієї «коробочки» є ще й паспорт: імʼя, тип і поточне значення. І найважливіше: тип фіксується під час оголошення і далі не змінюється на ходу. Це дисциплінує код і робить його передбачуванішим. Так, інколи це дратує, але частіше рятує від дивних багів.

Уявімо змінну як бейджик на конференції: на ньому написано імʼя (як до неї звертатися), «роль» (тип) і поточний «стан» (значення). Якщо на бейджику написано int, ви не зможете раптом вписати туди рядок "привіт" — охорона компілятора не пропустить.

Оголошення і присвоювання: чому це різні операції

На практиці новачки найчастіше плутаються не в синтаксисі, а в сенсі: чим відрізняється створення змінної від зміни значення вже створеної змінної. У Go ці дії розділені дуже чітко. Це як різниця між «завести нового персонажа в грі» і «змінити йому одяг»: імʼя персонажа зʼявляється під час створення, а далі ви вже оновлюєте його параметри.

У Go є три форми, які ми будемо використовувати щодня:

  1. var — оголошення змінної (створюємо імʼя в поточній області видимості).
  2. := — коротке оголошення змінної (також створюємо імʼя, зазвичай усередині функцій).
  3. = — присвоювання (імʼя вже має існувати, ми змінюємо значення).

Зведімо це в таблицю — так мозку простіше за щось зачепитися:

Запис Що робить Коли використовувати
var x int
оголошує x типу int, надає значення за замовчуванням коли хочемо явно вказати тип або оголосити заздалегідь
var x = 10
оголошує x, тип виводиться з 10 коли є стартове значення і тип очевидний
x := 10
оголошує x (тип виводиться), коротка форма зазвичай усередині main та інших функцій
x = 10
присвоює 10 уже наявному x коли x уже оголошено

І ось ключова фраза дня: var і := створюють змінну, а = — лише змінює значення.

2. Оголошення через var: два варіанти

var виглядає трохи офіційніше, ніж :=. Іноді це навіть приємно: код здається серйознішим, ніби ви одягли змінну в піджак. Але головне — var добре показує тип і стає у пригоді, коли ви хочете оголосити змінну заздалегідь, а заповнити її пізніше. Наприклад, після fmt.Scan або після перевірки умови.

var name Type: оголосили, значення за замовчуванням

Коли ви пишете var count int, ви створюєте змінну count типу int. Якщо не задати їй початкове значення, Go все одно не залишить її «зі сміттям», а встановить значення за замовчуванням. Для int це 0. Окремо ми ще розберемо значення за замовчуванням, але вже зараз важливо ось що: програма не читатиме випадкові дані з памʼяті.

Мінімальний приклад, щоб відчути різницю між оголошенням і присвоюванням:

package main

import "fmt"

func main() {
	var count int // оголосили
	count = 3     // присвоїли
	fmt.Println(count) // 3
}

Тут var count int створює змінну, а рядок count = 3 лише змінює її значення.

var name = expr: оголосили й одразу ініціалізували

Якщо ви пишете var x = 10, Go дивиться на праву частину й сам виводить тип. Це називається виведенням типу (type inference). Наприклад, якщо справа рядок, тип буде string. В офіційних матеріалах Go це часто пояснюють на дуже простому прикладі: var str = "Hello, 世界" означає те саме, що й var str string = "Hello, 世界".

Ось невеликий приклад:

package main

import "fmt"

func main() {
	var name = "Gopher"
	var age = 10
	fmt.Println(name, age) // Gopher 10
}

Тут name стає string, а ageint, бо Go виконав виведення типу за правою частиною.

3. := і = у повсякденному коді

Коротке оголошення := усередині функції

:= — це «швидке оголошення». Воно майже завжди використовується всередині main, бо там ми пишемо основний сценарій програми й хочеться менше церемоній. На практиці := робить код компактнішим і читабельнішим: ви одразу бачите і імʼя, і початкове значення. Це як сказати: «ось змінна, і ось її старт».

Приклад:

package main

import "fmt"

func main() {
	x := 10        // оголосили й ініціалізували
	x = x + 5      // оновили вже наявну змінну
	fmt.Println(x) // 15
}

Зверніть увагу на ритм: перший рядок створює змінну, далі ми використовуємо = для оновлення. Це дуже типовий стиль Go-коду.

Є важливе правило, яке поки що запам’ятаємо як мантру: якщо змінна вже існує і ви хочете її змінити — пишіть =. Якщо ви пишете :=, ви, найімовірніше, створюєте нову змінну. А це інколи не те, чого ви хотіли.

Присвоювання =: змінюємо значення наявної змінної

Оператор = не створює змінну. Він вимагає, щоб імʼя вже було оголошене в поточній області видимості або в зовнішній, яку видно. Тому x = 10 без попереднього оголошення x призведе до помилки компіляції. І це нормально: компілятор чесно каже «я не знаю, що таке x».

Приклад «правильної» послідовності:

package main

import "fmt"

func main() {
	var total int
	total = 100
	total = total - 30
	fmt.Println(total) // 70
}

Тут total створюється через var, а потім двічі оновлюється через =.

До речі, через суворість типів Go не любить автоматично змішувати різні числові типи. Якщо змінні мають різні типи, компілятор попросить вас бути точнішими. Це частина філософії Go: краще заздалегідь прибрати неоднозначність, ніж потім ловити дивні ефекти.

5. Область видимості блоків {}: де змінну «видно»

Тепер найцікавіше — і водночас те, що найчастіше викликає в новачків запитання: «Чому воно не працює?!». У Go фігурні дужки {} задають область видимості — ділянку коду, всередині якої імена змінних існують і доступні. Можна думати про блок як про кімнату: ви заносите в неї речі (змінні), користуєтеся ними всередині, але коли виходите, речі, створені в кімнаті, там і залишаються.

Схематично це виглядає так:

main {
    x  // живе у всьому main
    if {
        y // живе тільки всередині if
    }
    // тут y уже не існує
}

Це не «прискіпування Go», а захист від хаосу: якщо змінна потрібна лише всередині маленького блока, краще не давати їй жити довше. Так менше шансів випадково використати її не там, де слід.

Вкладені блоки if

Найпростіший приклад: змінна оголошена всередині if — зовні вона недоступна.

package main

import "fmt"

func main() {
	x := 10

	if x > 0 {
		y := x * 2
		fmt.Println("усередині:", y) // усередині: 20
	}

	fmt.Println("зовні:", x) // зовні: 10
}

Тут y існує лише всередині блока if. Це зручно: y — «тимчасовий розрахунок», він далі не потрібен.

Якщо спробувати написати fmt.Println(y) після if, компілятор скаже, що y не визначено. І це добре: компілятор буквально рятує вас від використання змінної, яка вже «померла».

Змінні циклу for

Із циклом for схожа історія: усе, що оголошено в заголовку циклу, зазвичай живе лише всередині нього.

package main

import "fmt"

func main() {
	for i := 1; i <= 3; i++ {
		fmt.Println(i) // 1, потім 2, потім 3
	}
}

Змінна i створена в межах циклу. Після завершення циклу вона вже не потрібна, і Go не дає їй «висіти» в main. Це робить код чистішим: менше зайвих імен, які можна випадково використати потім.

Коли оголошувати змінну зовні, а коли всередині

Тут корисна дуже практична логіка. Якщо значення потрібне після блока — змінну оголошують зовні, а всередині блока виконують присвоювання. Якщо значення потрібне лише для внутрішнього обчислення — оголошують прямо всередині блока, щоб воно не заважало решті програми.

Подивімося на різницю на одному прикладі: припустімо, ви хочете обчислити знижку, але використати її й після if.

package main

import "fmt"

func main() {
	total := 120
	discount := 0 // оголосили заздалегідь

	if total >= 100 {
		discount = 10 // присвоювання, НЕ оголошення
	}

	fmt.Println("discount:", discount) // discount: 10
}

Тут discount оголошена до if, бо нам важливо надрукувати її після умови. Усередині if ми не створюємо нову змінну, а оновлюємо наявну.

6. Мінізастосунок «BudgetBuddy»: змінні й області видимості

Давайте напишемо маленький консольний застосунок. Він буде простим, але корисним: рахуємо бюджет і витрати. Ми не використовуємо функції, колекції та складні структури — лише те, що вже вміємо: введення, змінні, if, for, виведення. Це буде як мінікаса або трекер витрат рівня «мамо, я програміст».

Версія 1: читаємо бюджет і одну покупку

Спочатку зробімо зовсім просту версію: користувач вводить бюджет і одну покупку, а програма друкує, скільки залишилося.

package main

import "fmt"

func main() {
	var budget int
	var purchase int
	fmt.Scan(&budget, &purchase)

	rest := budget - purchase
	fmt.Println(rest) // наприклад: 700
}

Зверніть увагу на стиль. Спершу вводимо значення у змінні, а потім рахуємо rest через :=, бо це проміжний результат і його тип очевидний.

Версія 2: цикл за N покупками і навіщо зовні потрібен spent

Тепер зробімо щось корисніше: нехай користувач вводить кількість покупок n, а потім вводить n сум. Нам потрібен накопичувач spent, який живе поза циклом, інакше після нього ми не зможемо ним скористатися.

package main

import "fmt"

func main() {
	var budget, n int
	fmt.Scan(&budget, &n)

	spent := 0
	for i := 0; i < n; i++ {
		var x int
		fmt.Scan(&x)
		spent = spent + x
	}
	fmt.Println(budget - spent)
}

Тут spent оголошено зовні циклу, бо він потрібен після нього. А от x — внутрішня змінна однієї ітерації: прочитали одну покупку, додали до суми, забули. Це хороший приклад вузької області видимості: чим менше змінних живе довго, тим менше плутанини.

Версія 3: перевірка бюджету через if

Додамо умову: якщо витратили більше бюджету — друкуємо попередження. Зауважте, що spent живе в усьому main, а отже доступний і після for, і всередині if.

package main

import "fmt"

func main() {
	var budget, n int
	fmt.Scan(&budget, &n)

	spent := 0
	for i := 0; i < n; i++ {
		var x int
		fmt.Scan(&x)
		spent = spent + x
	}

	if spent > budget {
		fmt.Println("витрати перевищили бюджет")
	}
	fmt.Println("витрачено:", spent)
}

Тут у нас два важливі ефекти. По-перше, spent оголошено рівно там, де він «починається» за змістом — перед циклом. По-друге, змінна x не розповзається по функції: вона потрібна на 2–3 рядки, от і живе 2–3 рядки.

7. Типові помилки під час оголошення та використання змінних

Помилка №1: використовувати = для імені, якого ще не існує.
Це одна з найчастіших ситуацій: людина пише sum = 0, бо «ну я ж присвоюю нуль», а sum до цього ніде не оголошував. У Go так не можна: спочатку оголошуємо (var sum int або sum := 0), а вже потім змінюємо (sum = sum + x). Хороша звичка — привчити себе бачити першу зустріч зі змінною: вона має бути через var або :=.

Помилка №2: очікувати, що змінна з if або for буде доступна після блока.
Психологічно це дуже зрозуміло: ви щойно обчислили y, вона така гарна, і хочеться використовувати її далі. Але якщо y оголошена всередині {}, то після закривальної дужки її ніби «видалили». Рішення тут просте за змістом: якщо значення потрібне після блока, оголосіть змінну до блока, а всередині зробіть присвоювання.

Помилка №3: оголошувати змінні занадто рано і занадто «широко».
Новачок інколи заводить на початку main десять змінних «про всяк випадок». Потім половина не використовується, половина використовується через 40 рядків — і ви вже не пам’ятаєте, хто є хто. У Go набагато приємніше оголошувати змінні ближче до місця використання: це зменшує область видимості й робить код легшим для читання. І для вашого майбутнього «я», яке за тиждень дивитиметься на код як на чужий.

Помилка №4: оголосити змінну й не використовувати її.
Go ставиться до цього суворо: якщо у функції є змінна, яка ніде не використовується, компілятор зазвичай лається. Спочатку це дратує («та я просто хотів на майбутнє!»), але швидко стає вашим союзником: він не дає тримати в коді сміття й «забуті задумки», які плутають читача. Якщо змінна реально не потрібна — видаляємо. Якщо потрібна пізніше — пишемо код так, щоб вона справді використовувалася.

Помилка №5: плутати «оголошення» й «оновлення» під час читання коду.
Інколи програма «не працює» просто тому, що ви думаєте, ніби рядок створює нову змінну, а він лише змінює стару — або навпаки. Допомагає звичка читати код як історію: «ось тут змінна зʼявилася», «ось тут їй змінили значення», «ось тут ми її використовуємо». Якщо ви навчитеся бачити ці три етапи, 80% проблем зі змінними підуть без магії та шаманства.

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