Прежде чем писать настоящие программы, нужно понять, как Go работает с данными. Данные в Go — это значения определённых типов: числа, строки, логические значения и многое другое. Чтобы с ними работать, мы используем переменные — именованные области памяти, в которых хранятся значения.
🔤 Типы данных в Go
Go — статически типизированный язык. Это означает, что тип переменной известен на этапе компиляции и не может измениться в процессе выполнения программы.
🔢 Числовые типы
Целые числа:
int
,int8
,int16
,int32
,int64
uint
,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: Конвертер единиц
Реализуйте конвертер, который:
- Переводит температуру между Цельсием, Фаренгейтом и Кельвином
- Использует правильные типы для каждого значения
- Обрабатывает случаи, когда результат может быть отрицательным
- Выводит результат с соответствующими единицами измерения