Перейти к основному содержимому

Типы данных в Go

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

1. Базовые типы (Primitive Types)

Логический тип (bool)

Самый простой тип. Может принимать только два значения: true (истина) или false (ложь).

  • Частота использования: Постоянно.
  • Когда использовать: Для хранения состояний, флагов, результатов сравнений и в условных конструкциях (if, for).
var isLoggedIn bool = true
var hasAccess bool = false

if isLoggedIn && !hasAccess {
fmt.Println("Пользователь вошел, но доступ запрещен.")
}

Целочисленные типы (int, uint, int8 и т.д.)

Предназначены для хранения целых чисел. Делятся на знаковые (int) и беззнаковые (uint).

  • Частота использования:
    • int: Постоянно. Это тип по умолчанию для целых чисел.
    • int64, uint32 и др.: По ситуации. Используются, когда важен точный размер (например, для баз данных, сетевых протоколов) или для экономии памяти в больших структурах.
  • Правило по умолчанию: Всегда используйте int, если нет веской причины выбрать другой тип.
// Самый распространенный случай
var counter int = 10

// Когда нужно гарантировать 64-битность (например, для ID из БД)
var userID int64 = 1234567890

// Когда значение не может быть отрицательным и невелико
var age uint8 = 25

Числа с плавающей точкой (float32, float64)

Предназначены для хранения вещественных чисел (с дробной частью).

  • float32: 32-битное число, меньшая точность.

  • float64: 64-битное число, двойная точность.

  • Частота использования:

    • float64: Часто.
    • float32: Редко. Используется в основном для экономии памяти, например, в 3D-графике.
  • Правило по умолчанию: Всегда используйте float64 для избежания проблем с точностью.

// Рекомендуемый тип для вещественных чисел
var price float64 = 199.99

// Менее точный тип
var temperature float32 = 36.6

Комплексные числа (complex64, complex128)

Предназначены для математических вычислений с мнимой частью. complex64 состоит из двух float32, а complex128 — из двух float64.

  • Частота использования: Крайне редко.
  • Когда использовать: В узкоспециализированных областях, таких как научные расчеты, обработка сигналов, физика. 99% разработчиков никогда не используют их в коммерческой разработке.
var c complex128 = complex(5, 2) // 5 + 2i
fmt.Println(c)

Строки (string)

Представляют собой последовательность байтов. В Go строки неизменяемы (immutable). Это означает, что после создания строки изменить ее содержимое нельзя, можно только создать новую.

  • Частота использования: Постоянно.
  • Когда использовать: Для хранения любого текста.
var name string = "Alice"
greeting := "Hello, " + name // Создается новая строка

// Строки неизменяемы
// greeting[0] = 'h' // Ошибка компиляции!

Важно: len(str) возвращает количество байт, а не символов. Для корректной работы с Unicode-символами используйте rune.

2. Составные типы (Composite Types)

Эти типы строятся на основе базовых и других составных типов.

Массивы ([n]T)

Коллекция элементов одного типа с фиксированной длиной. Размер массива является частью его типа, поэтому [5]int и [10]int — это два разных типа.

  • Частота использования: Редко.
  • Почему редко? Фиксированный размер делает их негибкими. Почти всегда вместо них используются слайсы.
  • Когда использовать: Когда вам нужно гарантировать, что коллекция имеет строго определённый размер (например, для хранения RGB-цвета [3]uint8).
var primaryColors [3]string
primaryColors[0] = "Red"
primaryColors[1] = "Green"
primaryColors[2] = "Blue"

// primaryColors[3] = "Yellow" // Ошибка: выход за пределы массива

Слайсы ([]T)

Гибкое и мощное представление последовательности элементов. Слайс — это "вид" или "окно" в базовый массив. Он имеет динамическую длину.

  • Частота использования: Постоянно. Это самый распространенный способ работы с коллекциями в Go.
  • Когда использовать: Практически всегда, когда вам нужна последовательность элементов.
// Создание слайса
var numbers []int // nil-слайс
scores := []int{10, 20, 30}
names := make([]string, 0, 10) // Длина 0, емкость 10

// Добавление элементов
scores = append(scores, 40) // scores теперь [10, 20, 30, 40]

Карты (map[K]V)

Неупорядоченная коллекция пар "ключ-значение". Все ключи должны быть одного типа, а все значения — другого.

  • Частота использования: Постоянно.
  • Когда использовать: Для хранения данных, которые удобно получать по уникальному ключу (ассоциативные массивы, хэш-таблицы, словари).
// Телефонная книга: ключ - string, значение - int
phonebook := make(map[string]int)

phonebook["Alice"] = 12345
phonebook["Bob"] = 67890

// Получение значения
bobsNumber, ok := phonebook["Bob"]
if ok {
fmt.Printf("Номер Боба: %d\n", bobsNumber)
}

Структуры (struct)

Композитный тип, который позволяет сгруппировать поля разных типов в единое целое. Это аналог классов (без методов) в других языках.

  • Частота использования: Постоянно.
  • Когда использовать: Для моделирования сущностей реального мира (пользователь, продукт, заказ) и создания собственных сложных типов данных.
type User struct {
ID int
Name string
IsActive bool
}

func main() {
user1 := User{
ID: 1,
Name: "John Doe",
IsActive: true,
}
fmt.Printf("Пользователь: %s (ID: %d)\n", user1.Name, user1.ID)
}

3. Ссылочные и специальные типы

Указатели (*T)

Переменная, которая хранит адрес в памяти другой переменной.

  • Частота использования: Часто.
  • Когда использовать:
    1. Чтобы разрешить функции изменять переданное ей значение.
    2. Чтобы избежать копирования больших структур при передаче в функцию (повышает производительность).
func deactivateUser(u *User) {
u.IsActive = false // Изменяем оригинальную структуру по указателю
}

func main() {
user1 := User{ID: 1, Name: "John", IsActive: true}
deactivateUser(&user1) // Передаем адрес переменной user1
fmt.Println(user1.IsActive) // Вывод: false
}

Интерфейсы (interface)

Тип, который определяет набор методов. Любой тип, который реализует все методы интерфейса, неявно удовлетворяет этому интерфейсу.

  • Частота использования: Постоянно. Это основа полиморфизма в Go.
  • Когда использовать: Для написания гибкого, слабо связанного кода, который работает с поведением, а не с конкретными типами.
// Интерфейс определяет поведение "умеет издавать звук"
type Speaker interface {
Speak() string
}

type Dog struct{}
func (d Dog) Speak() string { return "Woof!" }

type Cat struct{}
func (c Cat) Speak() string { return "Meow!" }

// Эта функция работает с любым типом, который удовлетворяет Speaker
func MakeSound(s Speaker) {
fmt.Println(s.Speak())
}

func main() {
MakeSound(Dog{}) // Woof!
MakeSound(Cat{}) // Meow!
}