$ sudo teach IT
МОДУЛЬ 2 · УРОК 2.1

Переменные и типы данных

Учимся хранить информацию в программе: числа, текст, логические значения и многое другое

В прошлом уроке мы написали первую программу — «Hello, World!» — и разобрались, как Go-код превращается в работающее приложение. Но та программа просто выводила текст и завершалась. Настоящие программы работают с данными: считают цены, хранят имена пользователей, сравнивают числа. Чтобы работать с данными, нужно уметь их где-то хранить. Для этого в программировании существуют переменные. Представьте коробку с наклейкой: на наклейке написано имя (название переменной), а внутри лежит значение (число, текст или что-то другое). Сегодня мы научимся создавать такие коробки, узнаем, какие бывают типы данных в Go, и напишем программу-калькулятор площади комнаты.

📦 Что такое переменная

Переменная — это именованное место в памяти компьютера, где хранится какое-то значение. Когда вы создаёте переменную, компьютер выделяет кусочек оперативной памяти и запоминает: «вот здесь лежит значение, а обращаться к нему нужно по такому-то имени».

Аналогия простая. Представьте складское помещение с полками. На каждой полке лежит что-то одно: коробка с яблоками, коробка с гвоздями, коробка с книгами. У каждой полки есть номер и подпись, чтобы кладовщик мог быстро найти нужное. Переменная — это такая подписанная полка. Имя переменной — это подпись, а значение — это то, что лежит на полке.

В Go есть несколько способов создать переменную. Давайте разберём каждый из них.

📝 Объявление переменной через var

Самый классический способ — использовать ключевое слово var. Синтаксис такой: пишем var, затем имя переменной, затем её тип, и (по желанию) присваиваем начальное значение.

package main

import "fmt"

func main() {
    var name string = "Алексей"
    var age int = 25
    var height float64 = 1.78
    var isStudent bool = true

    fmt.Println("Имя:", name)
    fmt.Println("Возраст:", age)
    fmt.Println("Рост:", height)
    fmt.Println("Студент:", isStudent)
}

Разберём каждую строку:

  • var name string = "Алексей" — создаём переменную name типа string (текст) со значением «Алексей»
  • var age int = 25 — переменная age типа int (целое число) со значением 25
  • var height float64 = 1.78 — переменная height типа float64 (дробное число) со значением 1.78
  • var isStudent bool = true — переменная isStudent типа bool (истина/ложь) со значением true

Go — умный язык. Если вы сразу присваиваете значение, можно не указывать тип — Go сам его определит. Это называется вывод типа (type inference):

var name = "Алексей"   // Go понимает: строка → тип string
var age = 25           // Go понимает: целое число → тип int
var height = 1.78      // Go понимает: дробное число → тип float64
var isStudent = true   // Go понимает: логическое значение → тип bool

Можно также объявить переменную без начального значения. В этом случае тип указывать обязательно — иначе Go не поймёт, что за переменную вы хотите:

var score int       // объявили переменную, но пока ничего не положили
score = 100         // теперь положили значение

Если нужно объявить несколько переменных разом, можно использовать блок var с круглыми скобками — так код выглядит чище:

var (
    firstName string = "Мария"
    lastName  string = "Иванова"
    age       int    = 30
)

⚡ Короткое объявление через :=

В Go есть более короткий и удобный способ объявить переменную — оператор :=. Его называют «коротким объявлением» (short variable declaration). Вместо var name string = "Алексей" можно просто написать:

name := "Алексей"
age := 25
height := 1.78
isStudent := true

Оператор := одновременно объявляет переменную и присваивает ей значение. Go автоматически определяет тип по правой части. Это самый популярный способ объявления переменных в Go — вы будете видеть его в коде постоянно.

💡 Важное правило: оператор := можно использовать только внутри функций. На уровне пакета (вне функций) — только var. Если вы попробуете написать name := "Алексей" вне функции main, компилятор выдаст ошибку.

Ещё один нюанс: := создаёт новую переменную. Если переменная уже существует, используйте обычный = для присвоения нового значения:

age := 25    // создаём переменную age
age = 26     // меняем значение (переменная уже существует)

// age := 30 — ОШИБКА! переменная age уже объявлена

Когда использовать var, а когда :=? Простое правило: если вы сразу знаете значение переменной — используйте :=. Если хотите объявить переменную без значения (чтобы присвоить его позже) — используйте var.

🔒 Константы — значения, которые не меняются

Иногда вам нужно сохранить значение, которое никогда не должно меняться. Например, число Пи всегда равно 3.14159..., количество месяцев в году всегда 12, а скорость света — 299 792 458 м/с. Для таких значений в Go есть константы.

Константы объявляются через ключевое слово const:

const pi = 3.14159
const maxStudents = 30
const courseName = "Основы Go"

Если попытаетесь изменить константу, Go выдаст ошибку при компиляции:

const pi = 3.14159
pi = 3.14  // ОШИБКА: cannot assign to pi (constant)

Как и с var, несколько констант можно объединить в блок:

const (
    pi         = 3.14159
    maxRetries = 5
    appName    = "MyApp"
)

💡 Подсказка: константы в Go — это не просто «переменные, которые нельзя менять». Они вычисляются во время компиляции, а не во время выполнения программы. Это значит, что константами могут быть только простые значения: числа, строки, логические значения. Нельзя, например, сделать константой результат вызова функции.

🧱 Базовые типы данных

В Go каждая переменная имеет тип. Тип определяет, какие данные можно хранить в переменной и какие операции с ней допустимы. Нельзя, например, сложить число с текстом — Go не позволит. Это называется статическая типизация, и это одно из главных преимуществ Go: многие ошибки обнаруживаются ещё до запуска программы.

Разберём четыре базовых типа, с которыми вы будете работать чаще всего.

int — целые числа

Тип int хранит целые числа: положительные, отрицательные и ноль. Это числа без дробной части: 1, 42, -10, 0, 1000000. Используется для подсчёта чего-либо, индексов, возраста, количества и других целочисленных значений.

count := 10
temperature := -5
year := 2026

fmt.Println(count + year)       // 2036
fmt.Println(temperature * 2)    // -10

float64 — дробные числа

Тип float64 хранит числа с дробной частью (числа с плавающей точкой): 3.14, 0.5, -2.7, 100.0. Число 64 в названии означает, что для хранения используется 64 бита памяти — это обеспечивает высокую точность. Используется для цен, расстояний, координат, научных расчётов.

price := 99.99
pi := 3.14159
weight := 72.5

fmt.Println(price * 2)    // 199.98
fmt.Println(pi * 10)      // 31.4159

string — текст (строки)

Тип string хранит текст — последовательность символов. Строки всегда записываются в двойных кавычках. Это могут быть имена, сообщения, адреса, любой текст. Строки можно складывать (конкатенировать) — при этом они просто склеиваются.

firstName := "Иван"
lastName := "Петров"
fullName := firstName + " " + lastName

fmt.Println(fullName)    // Иван Петров
fmt.Println("Привет, " + firstName + "!")  // Привет, Иван!

bool — логический тип (да/нет)

Тип bool хранит только два значения: true (истина) или false (ложь). Это самый простой тип данных, но невероятно важный. Он используется для условий: «пользователь авторизован?», «заказ оплачен?», «число больше нуля?».

isOnline := true
hasAccess := false

fmt.Println(isOnline)     // true
fmt.Println(hasAccess)    // false
fmt.Println(10 > 5)       // true (результат сравнения — тоже bool)
fmt.Println(3 == 7)       // false

🎯 Нулевые значения — фишка Go

Вот одна из вещей, которая делает Go особенным и отличает его от многих других языков. В Go каждая переменная, которой не присвоено значение, автоматически получает так называемое нулевое значение (zero value). Переменная никогда не бывает «пустой» или «неопределённой» — у неё всегда есть значение.

Вот какие нулевые значения получают разные типы:

  • int → 0 (ноль)
  • float64 → 0.0 (ноль с точкой)
  • string → "" (пустая строка)
  • bool → false (ложь)

Давайте убедимся в этом на практике:

package main

import "fmt"

func main() {
    var number int
    var decimal float64
    var text string
    var flag bool

    fmt.Println("int:", number)      // int: 0
    fmt.Println("float64:", decimal) // float64: 0
    fmt.Println("string:", text)     // string:
    fmt.Println("bool:", flag)       // bool: false
}

Мы объявили четыре переменные через var, но не присвоили им никаких значений. Запустили программу — и всё работает, никаких ошибок. Каждая переменная получила своё нулевое значение.

Почему это важно? Во многих языках программирования (например, в C или C++) неинициализированная переменная содержит «мусор» — случайные данные, которые остались в памяти от предыдущих программ. Это может приводить к непредсказуемым ошибкам. В Go такая ситуация невозможна: каждая переменная гарантированно имеет осмысленное начальное значение.

💡 Запомните: в Go нет понятия «неинициализированная переменная». Если вы объявили переменную и не задали ей значение — она не пустая и не сломанная. Она содержит нулевое значение своего типа. Это важный принцип дизайна Go, который делает программы безопаснее и предсказуемее.

🔄 Явное приведение типов

В Go нельзя просто так смешивать разные типы данных. Например, нельзя сложить int и float64 напрямую — даже если математически это кажется логичным. Go строг к типам, и это защищает вас от скрытых ошибок.

a := 10       // int
b := 3.5      // float64

// c := a + b  — ОШИБКА! Нельзя сложить int и float64

Чтобы выполнить такую операцию, нужно явно преобразовать один тип в другой. В Go для этого используется синтаксис тип(значение):

a := 10
b := 3.5

c := float64(a) + b   // Преобразовали a из int в float64
fmt.Println(c)         // 13.5

Можно и наоборот — преобразовать float64 в int. Но будьте внимательны: дробная часть просто отбрасывается (не округляется!):

price := 99.99
rounded := int(price)
fmt.Println(rounded)   // 99 (дробная часть .99 потеряна!)

Ещё один частый пример — преобразование между числами и строками. Но тут всё не так просто. Нельзя написать string(65) и получить «65» — Go не так работает. Вместо этого string(65) вернёт символ с кодом 65, а это буква «A».

Для превращения числа в строку используется пакет strconv (мы подробнее разберём его в следующих уроках) или функция fmt.Sprintf:

package main

import "fmt"

func main() {
    age := 25
    message := fmt.Sprintf("Мне %d лет", age)
    fmt.Println(message)   // Мне 25 лет
}

Давайте посмотрим на более полный пример приведения типов:

package main

import "fmt"

func main() {
    // int → float64
    whole := 42
    decimal := float64(whole)
    fmt.Println(decimal)   // 42

    // float64 → int (дробная часть отбрасывается!)
    pi := 3.99
    truncated := int(pi)
    fmt.Println(truncated) // 3

    // int → другой числовой тип
    big := 1000
    small := int8(big)     // int8 хранит числа от -128 до 127
    fmt.Println(small)     // -24 (переполнение! будьте осторожны)
}

💡 Подсказка: Go требует явного приведения типов, и это не недостаток, а преимущество. Когда вы видите float64(a) в коде, вы сразу понимаете: «здесь число преобразуется в дробное». Ничего не происходит «за кулисами», всё явно и прозрачно. Это делает Go-код легко читаемым.

🏷 Как правильно называть переменные

Хорошие имена переменных — это как понятные подписи на коробках. Если написать «X1» — непонятно, что внутри. Если написать «возрастПользователя» — сразу всё ясно. Вот несколько правил именования в Go:

  • Имя начинается с буквы или подчёркивания _
  • Может содержать буквы, цифры и подчёркивания
  • Регистр имеет значение: age и Age — это разные переменные
  • Нельзя использовать ключевые слова Go: var, func, if, for и т.д.
  • В Go принят стиль camelCase: firstName, maxRetries, isActive
// Хорошие имена — понятно, что хранится
userName := "Иван"
totalPrice := 1500.50
isLoggedIn := true
maxAttempts := 3

// Плохие имена — непонятно, что это
x := "Иван"
tp := 1500.50
f := true
m := 3

💡 Подсказка: в Go есть особое соглашение. Если имя переменной начинается с заглавной буквы (Age) — она доступна из других пакетов (экспортируемая). Если со строчной (age) — только внутри текущего пакета (неэкспортируемая). Пока не переживайте об этом — мы разберём пакеты позже. Просто начинайте имена со строчной буквы.

🚫 Неиспользуемые переменные — ошибка компиляции

В Go есть строгое правило: если вы объявили переменную, вы обязаны её использовать. Иначе программа просто не скомпилируется. Это может удивить новичков, но за этим стоит здравый смысл: неиспользуемая переменная — это либо ошибка (вы забыли что-то с ней сделать), либо мусор (переменная больше не нужна, но вы забыли её удалить).

package main

func main() {
    name := "Алексей"
    // Мы не используем name — ОШИБКА!
    // name declared and not used
}

Чтобы код скомпилировался, нужно либо использовать переменную (напечатать, передать в функцию, выполнить операцию), либо удалить её. Если по какой-то причине вам нужно проигнорировать значение, используйте специальный идентификатор _ (подчёркивание):

_ = "это значение мы намеренно игнорируем"

Подчёркивание _ — это «чёрная дыра» в Go. Всё, что вы туда присваиваете, просто исчезает. Это полезно, когда функция возвращает несколько значений, а вам нужно только одно. Но об этом позже.

🛠 Практика: калькулятор площади комнаты

Давайте применим всё, что мы изучили, на практике. Напишем программу, которая вычисляет площадь комнаты и стоимость напольного покрытия.

Создайте новый файл area.go и введите следующий код:

package main

import "fmt"

func main() {
    // Размеры комнаты (в метрах)
    length := 5.5
    width := 3.8

    // Вычисляем площадь
    area := length * width

    // Цена за квадратный метр ламината (в рублях)
    const pricePerSquareMeter = 850.0

    // Общая стоимость
    totalCost := area * pricePerSquareMeter

    // Информация о комнате
    roomName := "Гостиная"
    needsRepair := true

    // Выводим результат
    fmt.Println("=== Калькулятор площади ===")
    fmt.Println("Комната:", roomName)
    fmt.Println("Длина:", length, "м")
    fmt.Println("Ширина:", width, "м")
    fmt.Println("Площадь:", area, "кв.м")
    fmt.Println("Цена за кв.м:", pricePerSquareMeter, "руб.")
    fmt.Println("Общая стоимость:", totalCost, "руб.")
    fmt.Println("Нужен ремонт:", needsRepair)
}

Запустите программу:

go run area.go

Вы увидите результат:

=== Калькулятор площади ===
Комната: Гостиная
Длина: 5.5 м
Ширина: 3.8 м
Площадь: 20.9 кв.м
Цена за кв.м: 850 руб.
Общая стоимость: 17765 руб.
Нужен ремонт: true

Разберём, что здесь использовано:

  • length и width — переменные float64 (дробные числа, потому что размеры комнаты редко бывают целыми)
  • area — вычисленная переменная (результат умножения)
  • pricePerSquareMeter — константа (цена не меняется по ходу программы)
  • totalCost — ещё одна вычисленная переменная
  • roomName — переменная string (текст)
  • needsRepair — переменная bool (логическое значение)

Теперь усложним программу. Добавим вторую комнату и посчитаем общую площадь:

package main

import "fmt"

func main() {
    // Комната 1
    room1Name := "Гостиная"
    room1Length := 5.5
    room1Width := 3.8
    room1Area := room1Length * room1Width

    // Комната 2
    room2Name := "Спальня"
    room2Length := 4.2
    room2Width := 3.0
    room2Area := room2Length * room2Width

    // Общая площадь
    totalArea := room1Area + room2Area

    // Стоимость
    const pricePerSqM = 850.0
    totalCost := totalArea * pricePerSqM

    // Вывод
    fmt.Println("=== Калькулятор площади квартиры ===")
    fmt.Println()
    fmt.Println(room1Name+":", room1Area, "кв.м")
    fmt.Println(room2Name+":", room2Area, "кв.м")
    fmt.Println()
    fmt.Println("Общая площадь:", totalArea, "кв.м")
    fmt.Println("Стоимость покрытия:", totalCost, "руб.")

    // Проверяем, укладываемся ли в бюджет
    var budget float64 = 30000.0
    withinBudget := totalCost <= budget
    fmt.Println("Бюджет:", budget, "руб.")
    fmt.Println("Укладываемся:", withinBudget)
}

Результат:

=== Калькулятор площади квартиры ===

Гостиная: 20.9 кв.м
Спальня: 12.6 кв.м

Общая площадь: 33.5 кв.м
Стоимость покрытия: 28475 руб.
Бюджет: 30000 руб.
Укладываемся: true

Обратите внимание на строку withinBudget := totalCost <= budget. Оператор <= сравнивает два числа и возвращает bool: true, если стоимость меньше или равна бюджету. Здесь мы использовали все четыре базовых типа: float64 для размеров и цен, string для названий комнат, bool для результата сравнения, а константа заменила «магическое число».

✏️ Задание для самостоятельной работы

Попробуйте написать свою программу на основе того, что мы изучили. Вот идея:

Калькулятор стоимости поездки. Создайте переменные:

  • distance — расстояние в километрах (float64)
  • fuelConsumption — расход топлива на 100 км (float64)
  • fuelPrice — цена за литр бензина (const float64)
  • passengers — количество пассажиров (int)
  • destination — название города (string)

Посчитайте: сколько литров бензина нужно, общую стоимость, стоимость на одного пассажира. Выведите всё на экран.

Не бойтесь экспериментировать! Измените значения переменных, добавьте новые, попробуйте использовать разные типы. Чем больше практики — тем быстрее всё станет привычным.

📋 Итоги урока

  • Переменная — именованное место в памяти для хранения данных
  • var name type = value — полное объявление, name := value — короткое (только внутри функций)
  • const — неизменяемые значения, которые нельзя перезаписать
  • Четыре базовых типа: int (целые числа), float64 (дробные), string (текст), bool (true/false)
  • Нулевые значения (zero values): int → 0, float64 → 0.0, string → "", bool → false
  • Go требует явного приведения типов: float64(myInt), int(myFloat)
  • Неиспользуемые переменные — ошибка компиляции. Go не позволяет мусорить
  • Используйте понятные имена в стиле camelCase: userName, totalPrice, isActive

В следующем уроке мы научимся общаться с пользователем: считывать данные с клавиатуры и красиво их выводить. Разберём пакет fmt во всех деталях — форматированный вывод, чтение ввода и превращение нашего калькулятора из «жёстко запрограммированного» в интерактивный, где пользователь сам вводит размеры комнаты.

Переменные и типы данных — Язык Go – для начинающих! | $ sudo teach IT