Прежде чем писать настоящие программы, нужно понять, как Go работает с данными. Данные в Go — это значения определённых типов: числа, строки, логические значения и многое другое. Чтобы с ними работать, мы используем переменные — именованные области памяти, в которых хранятся значения.
🔤 Типы данных в Go
Go — статически типизированный язык. Это означает, что тип переменной известен на этапе компиляции и не может измениться в процессе выполнения программы.
🔢 Числовые типы
Целые числа:
int,int8,int16,int32,int64uint,uint8(илиbyte),uint16,uint32,uint64
Числа с плавающей точкой:
float32,float64
Комплексные числа:
complex64,complex128
var a int = 10
var b uint8 = 255
var c float64 = 3.14
var d complex64 = 2 + 3i
💡 В чём отличия int от uint?
int может хранить как положительные, так и отрицательные числа (например, -10, 0, 42), а uint — только положительные (unsigned integer - беззнаковое число), но с удвоенным верхним пределом. Используй uint, если заранее знаешь, что переменная не будет принимать отрицательные >значения, например, счётчик или индекс.
🔍 Что означают числа в типах int8, uint32 и т. д.?
Число в конце типа указывает на размер в битах, который выделяется для хранения значения:
int8 — 8 бит → диапазон от -128 до 127
uint8 — 8 бит → от 0 до 255
int16 — 16 бит → от -32 768 до 32 767
int32 — 32 бита → от -2,147,483,648 до 2,147,483,647
uint32 — от 0 до 4,294,967,295
int64, uint64 — используются для очень больших чисел
Чем больше битов — тем больше диапазон значений, но и выше потребление памяти. Обычно достаточно использовать int, если нет особых требований.
🔡 Строки
var message string = "Привет, Go!"
Строки — это неизменяемые последовательности байт. В Go они представлены в кодировке UTF-8.
✅ Булевы значения
var isReady bool = true
bool может принимать только два значения: true или false.
🕳 Специальный тип rune
rune — это псевдоним для int32, используется для представления символов Unicode:
var symbol rune = 'Ж'
📝 Объявление переменных
Переменные в Go можно объявлять несколькими способами.
Способ 1: Использование var
var x int = 10
var name string = "Go"
var active bool // значение по умолчанию: false
Можно опустить указание типа, если значение явно задано:
var city = "Москва" // Go сам выведет тип string
Способ 2: Краткая форма :=
count := 5
language := "Go"
success := true
💡
:=— это синтаксический сахар, удобный для быстрой инициализации переменных. Он:
- Используется только внутри функций.
 - Требует обязательной инициализации (значения не может не быть).
 - Go автоматически определяет тип на основе значения справа.
 
pi := 3.1415        // float64
name := "Гоша"       // string
ok := false         // bool
💡 Под капотом
x := 10эквивалентноvar x int = 10.
🛠 Константы
Константы объявляются с помощью ключевого слова const и не могут изменяться после определения:
const pi float64 = 3.14159
const appName = "MyApp"
Константы полезны для значений, которые не должны меняться, например, математические константы или имена приложений. Их тип можно опустить, если значение задано явно:
const maxRetries = 3 // Go выведет тип int
🧪 Пример
package main
import "fmt"
const (
    gravity  = 9.81 // ускорение свободного падения, м/с²
    distance = 100   // расстояние, м
)
func main() {
    time := float64(distance) / gravity
    fmt.Printf("Время падения: %.2f секунд\n", time)
}
📦 Значения по умолчанию
Если переменная объявлена через var, но не инициализирована, она получает значение по умолчанию:
| Тип | Значение по умолчанию | 
|---|---|
int | 0 | 
float64 | 0.0 | 
string | "" (пустая строка) | 
bool | false | 
pointer | nil | 
🧪 Пример
package main
import "fmt"
func main() {
    var age int = 28
    name := "Анна"
    score := 95.5
    var passed bool = true
    fmt.Println("Имя:", name)
    fmt.Println("Возраст:", age)
    fmt.Println("Оценка:", score)
    fmt.Println("Сдала экзамен?", passed)
}
Результат:
Имя: Анна
Возраст: 28
Оценка: 95.5
Сдала экзамен? true
🔄 Преобразование типов
В Go нет автоматического преобразования типов. Чтобы преобразовать один тип в другой, используйте явное приведение:
var i int = 42
var f float64 = float64(i)
var u uint = uint(f)
fmt.Println(f) // 42.0
💡 Будьте осторожны: преобразование может привести к потере данных (например, при приведении
float64кintдробная часть отбрасывается).
🚫 Типичные ошибки
Ошибка: Использование := вне функции.
package main
count := 10 // Ошибка: := можно использовать только внутри функций
Исправление:
var count = 10 // Используйте var для глобальных переменных
Ошибка: Попытка сложения переменных разных типов.
var i int = 42
var f float64 = 3.14
result := i + f // Ошибка: типы несовместимы
Исправление:
result := float64(i) + f // Приведение int к float64
fmt.Println(result) // 45.14
🚀 Практические примеры работы с типами
Система управления температурой
Рассмотрим практическое применение различных типов данных в реальном сценарии — системе мониторинга температуры:
package main
import (
    "fmt"
    "math"
)
// Константы для температурных пределов
const (
    MinSafeTemp     = -10.0  // Минимальная безопасная температура
    MaxSafeTemp     = 35.0   // Максимальная безопасная температура
    CriticalTempDiff = 5.0   // Критическая разница температур
)
func main() {
    // Текущие показания датчиков (float64 для точности)
    currentTemp := 24.7
    previousTemp := 22.3
    
    // Номер датчика и счётчик измерений (int)
    sensorID := 1001
    measurementCount := 0
    
    // Статус системы (bool)
    isSystemActive := true
    alertSent := false
    
    fmt.Printf("🌡️  Система мониторинга температуры\n")
    fmt.Printf("Датчик #%d активен: %t\n", sensorID, isSystemActive)
    
    if isSystemActive {
        measurementCount++
        
        // Анализ температуры
        tempDiff := math.Abs(currentTemp - previousTemp)
        
        fmt.Printf("\n📊 Показания датчика:\n")
        fmt.Printf("   Текущая температура: %.1f°C\n", currentTemp)
        fmt.Printf("   Предыдущая: %.1f°C\n", previousTemp)
        fmt.Printf("   Изменение: %.1f°C\n", tempDiff)
        
        // Проверка безопасных пределов
        if currentTemp < MinSafeTemp || currentTemp > MaxSafeTemp {
            fmt.Printf("⚠️  ВНИМАНИЕ: Температура за пределами нормы!\n")
            alertSent = true
        }
        
        // Проверка резких изменений
        if tempDiff > CriticalTempDiff {
            fmt.Printf("🚨 КРИТИЧНО: Резкое изменение температуры!\n")
            alertSent = true
        }
        
        if !alertSent {
            fmt.Printf("✅ Температура в норме\n")
        }
        
        fmt.Printf("\n📈 Статистика: измерение #%d\n", measurementCount)
    }
}
Конвертер валют с безопасным преобразованием типов
Этот пример показывает важность правильного преобразования типов при работе с денежными суммами:
package main
import (
    "fmt"
    "math"
)
func convertCurrency(amount float64, rate float64, precision int) {
    // Конвертируем валюту
    converted := amount * rate
    
    // Округляем до нужной точности (копейки/центы)
    multiplier := math.Pow(10, float64(precision))
    rounded := math.Round(converted * multiplier) / multiplier
    
    fmt.Printf("💰 %.2f → %.2f (курс: %.4f)\n", amount, rounded, rate)
    
    // Безопасное преобразование в копейки для точных расчётов
    kopecks := int64(rounded * 100) // Преобразуем в копейки
    rubles := kopecks / 100         // Целые рубли
    remainder := kopecks % 100      // Оставшиеся копейки
    
    fmt.Printf("💸 В копейках: %d (рублей: %d, копеек: %d)\n", 
        kopecks, rubles, remainder)
}
func main() {
    fmt.Println("🏦 Конвертер валют")
    
    // Исходная сумма в долларах
    dollars := 125.75
    
    // Курс доллара к рублю
    usdToRub := 93.2156
    
    convertCurrency(dollars, usdToRub, 2)
}
Обработка текста с Unicode символами (rune)
Демонстрирует важность типа rune при работе с многоязычным текстом:
package main
import (
    "fmt"
    "unicode/utf8"
)
func analyzeText(text string) {
    fmt.Printf("📝 Анализ текста: \"%s\"\n", text)
    
    // Количество байт в строке
    byteCount := len(text)
    
    // Количество символов Unicode (rune)
    runeCount := utf8.RuneCountInString(text)
    
    fmt.Printf("📊 Статистика:\n")
    fmt.Printf("   Байт: %d\n", byteCount)
    fmt.Printf("   Символов: %d\n", runeCount)
    
    if byteCount != runeCount {
        fmt.Printf("   💡 Текст содержит многобайтовые символы\n")
    }
    
    // Детальный анализ каждого символа
    fmt.Printf("\n🔍 Разбор по символам:\n")
    for i, r := range text {
        // r имеет тип rune (int32)
        byteSize := utf8.RuneLen(r)
        fmt.Printf("   Позиция %d: '%c' (код: %d, байт: %d)\n", 
            i, r, int(r), byteSize)
    }
}
func main() {
    // Тестируем с разными видами текста
    texts := []string{
        "Hello",           // Только ASCII
        "Привет",          // Кириллица
        "🌟 Go язык! 🚀",   // Эмодзи и смешанные символы
    }
    
    for _, text := range texts {
        analyzeText(text)
        fmt.Println()
    }
}
Выбор правильного целочисленного типа
Пример показывает, когда и какой целочисленный тип использовать:
package main
import "fmt"
func demonstrateIntTypes() {
    // uint8 для небольших положительных значений (0-255)
    var brightness uint8 = 128 // Яркость экрана 0-255
    
    // int16 для небольших знаковых значений (-32768 до 32767)
    var temperature int16 = -15 // Температура в градусах Цельсия
    
    // uint32 для больших положительных значений
    var userID uint32 = 1_000_000 // ID пользователя
    
    // int64 для временных меток и больших значений
    var timestamp int64 = 1640995200 // Unix timestamp
    
    // int для обычных случаев (размер зависит от архитектуры)
    var itemCount int = 42 // Количество элементов
    
    fmt.Printf("🔢 Демонстрация типов:\n")
    fmt.Printf("Яркость (uint8): %d (размер: %d байт)\n", 
        brightness, 1)
    fmt.Printf("Температура (int16): %d°C (размер: %d байта)\n", 
        temperature, 2)
    fmt.Printf("ID пользователя (uint32): %d (размер: %d байта)\n", 
        userID, 4)
    fmt.Printf("Timestamp (int64): %d (размер: %d байт)\n", 
        timestamp, 8)
    fmt.Printf("Количество (int): %d\n", itemCount)
    
    // Демонстрация переполнения
    fmt.Printf("\n⚠️  Демонстрация переполнения uint8:\n")
    var counter uint8 = 254
    fmt.Printf("Было: %d\n", counter)
    counter++
    fmt.Printf("Стало: %d\n", counter) // 255
    counter++
    fmt.Printf("После переполнения: %d\n", counter) // 0 (переполнение!)
}
func main() {
    demonstrateIntTypes()
}
🧠 Проверь свои знания
- Назови как минимум три вида числовых типов в Go и их отличия.
 - В чём разница между 
varи:=? - Что произойдёт, если объявить переменную через 
var, но не задать ей значение? - Что такое 
runeи зачем он нужен? - Напиши программу, которая объявляет переменные разных типов и выводит их значения.
 - Попробуй преобразовать 
float64вintи объясни, что происходит с дробной частью. - Почему важно выбирать правильный размер целочисленного типа?
 - Как правильно работать с денежными суммами, чтобы избежать ошибок округления?
 
📌 Главное из главы
- Go — статически типизированный язык с автоматическим выводом типов
 - Числовые типы различаются размером и знаковостью (int8-int64, uint8-uint64)
 - Строки в Go неизменяемы и используют кодировку UTF-8
 runeкритически важен для правильной работы с Unicode символами:=удобен для локальных переменных,varдля глобальных и сложных случаев- Преобразование типов всегда явное — Go не делает автоматических конверсий
 - Выбор правильного типа влияет на память, производительность и корректность
 
🛠 Практические упражнения
Упражнение 1: Калькулятор площади
Напишите программу, которая:
- Объявляет переменные для длины и ширины прямоугольника (float64)
 - Вычисляет площадь и периметр
 - Выводит результаты с точностью до 2 знаков после запятой
 - Преобразует площадь в квадратные сантиметры (если размеры в метрах)
 
Упражнение 2: Анализатор возраста
Создайте программу, которая:
- Принимает возраст пользователя (uint8)
 - Определяет категорию: ребёнок (<18), взрослый (18-65), пенсионер (>65)
 - Вычисляет, сколько дней пользователь прожил
 - Использует константы для границ возрастных категорий
 
Упражнение 3: Конвертер единиц
Реализуйте конвертер, который:
- Переводит температуру между Цельсием, Фаренгейтом и Кельвином
 - Использует правильные типы для каждого значения
 - Обрабатывает случаи, когда результат может быть отрицательным
 - Выводит результат с соответствующими единицами измерения