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

Составные типы (composite types): Массивы, Слайсы, Карты и Структуры

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

Массив — это пронумерованная последовательность элементов одного типа с фиксированной длиной.

Аналогия: Контейнер для таблеток на неделю. В нем ровно 7 ячеек, ни больше, ни меньше. Вы не можете добавить восьмую ячейку, не взяв новый, больший контейнер.

Ключевые особенности:

  • Фиксированный размер: Длина массива является частью его типа. [5]int и [10]int — это два совершенно разных, несовместимых типа.

  • Значимый тип (Value Type): При присваивании или передаче в функцию создается полная копия массива. Это может быть неэффективно для больших массивов.

  • Частота использования: Редко.

  • Когда использовать: Только тогда, когда вам нужно гарантировать, что коллекция имеет строго определенный, неизменяемый размер. Например, для хранения цвета RGB ([3]uint8) или координат ([2]float64).

// Массив из 3-х строк. Его длина (3) — часть типа.
var weekDays [3]string

weekDays[0] = "Понедельник"
weekDays[1] = "Вторник"
weekDays[2] = "Среда"

// weekDays[3] = "Четверг" // Ошибка компиляции: выход за пределы массива

// Создание копии
anotherWeek := weekDays
anotherWeek[0] = "Sunday"

fmt.Println(weekDays[0]) // Вывод: Понедельник (оригинал не изменился)
fmt.Println(anotherWeek[0]) // Вывод: Sunday

2. Слайсы ([]T)

Слайс (срез) — это гибкое и мощное "окно" или "вид" на элементы базового массива. В отличие от массива, слайс имеет динамическую длину.

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

Ключевые особенности:

  • Динамический размер: Длина слайса не является частью его типа. Вы можете легко добавлять элементы с помощью функции append.

  • Ссылочный тип: При присваивании или передаче в функцию копируется только небольшой заголовок слайса, но он по-прежнему указывает на тот же самый базовый массив. Изменение элемента в копии слайса изменит его и в оригинале.

  • Частота использования: Постоянно. Это самый распространенный и идиоматичный способ работы с последовательностями в Go.

  • Когда использовать: Практически всегда, когда вам нужна коллекция элементов.

// Создаем слайс с 3 элементами
scores := []int{10, 20, 30}
fmt.Printf("Длина: %d, Емкость: %d\n", len(scores), cap(scores)) // Длина: 3, Емкость: 3

// Добавляем новый элемент. Go может создать новый, больший массив под капотом.
scores = append(scores, 40)
fmt.Printf("Длина: %d, Емкость: %d\n", len(scores), cap(scores)) // Длина: 4, Емкость: 6 (емкость выросла)

// Слайсы - ссылочные типы
subSlice := scores[1:3] // [20, 30]
subSlice[0] = 99 // Изменяем элемент в subSlice

fmt.Println(scores) // Вывод: [10 99 30 40] (оригинальный слайс тоже изменился!)

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

Карта (map) — это неупорядоченная коллекция пар "ключ-значение".

Аналогия: Словарь или телефонная книга. Вы ищете уникальное слово (ключ), чтобы получить его определение (значение). Порядок слов в словаре не важен для поиска.

Ключевые особенности:

  • Неупорядоченность: При итерации по карте порядок элементов не гарантируется.

  • Уникальные ключи: Все ключи в карте должны быть уникальными. Тип ключа должен поддерживать операции сравнения (== и !=).

  • Ссылочный тип: Как и слайсы, карты являются ссылочным типом.

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

  • Когда использовать: Для хранения данных, которые нужно быстро находить по уникальному идентификатору. Идеально подходит для кэшей, словарей, наборов (sets) и т.д.

// Создаем карту, где ключ - string, а значение - int
ages := make(map[string]int)

ages["Alice"] = 30
ages["Bob"] = 25

// Получение значения и проверка его существования ("comma ok" idiom)
age, ok := ages["Alice"]
if ok {
fmt.Printf("Возраст Алисы: %d\n", age) // Возраст Алисы: 30
}

// Проверка несуществующего ключа
_, ok = ages["Charlie"]
if !ok {
fmt.Println("Чарли не найден.") // Чарли не найден.
}

// Удаление элемента
delete(ages, "Bob")
fmt.Println(ages) // map[Alice:30]

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

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

Аналогия: Анкета или карточка сотрудника. В ней есть разные поля: "Имя" (строка), "Возраст" (число), "Активен" (логический тип).

Ключевые особенности:

  • Композиция: Позволяет объединять разнородные данные.

  • Значимый тип (Value Type): Как и массивы, структуры по умолчанию копируются при передаче. Поэтому для эффективной работы их часто передают по указателю (*User).

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

  • Когда использовать: Для моделирования сущностей из реального мира (Пользователь, Продукт, Заказ) или любой другой логически связанной группы данных.

type User struct {
ID int64
Name string
Email string
IsActive bool
}

func main() {
user1 := User{
ID: 1,
Name: "John Doe",
Email: "john.doe@example.com",
IsActive: true,
}

// Доступ к полям через точку
fmt.Printf("Имя пользователя: %s\n", user1.Name)

// Если мы передадим user1 в функцию, будет создана копия.
// Чтобы изменить оригинал, нужно передать указатель.
deactivate(&user1)
fmt.Printf("Активен ли пользователь: %v\n", user1.IsActive) // Активен ли пользователь: false
}

func deactivate(u *User) {
u.IsActive = false
}

Итоговая таблица

ТипСтруктураРазмерПередача в функциюОсновное применение
МассивУпорядоченная коллекцияФиксированныйКопируется всё содержимоеКогда размер известен и никогда не меняется (редко)
СлайсУпорядоченная коллекцияДинамическийКопируется заголовок (ссылочный тип)Основной тип для работы с последовательностями
КартаНеупорядоченная коллекцияДинамическийКопируется заголовок (ссылочный тип)Хранение и поиск по уникальному ключу
СтруктураКоллекция именованных полейФиксированныйКопируется всё содержимоеСоздание собственных типов данных, моделирование сущностей