Структуры данных — это способы организации данных в программе. В Go встроены мощные и простые структуры: массивы, срезы (slices), карты (maps) и структуры (structs). Они позволяют хранить и обрабатывать данные, от списков чисел до сложных объектов, таких как профили пользователей.
💬 Зачем это нужно?
Представь, что ты хранишь информацию о книгах: название, автор, количество страниц. Вместо множества переменных лучше использовать структуры данных, чтобы всё было организовано и удобно.
🗃 Массивы
Массивы — это наборы элементов одного типа с фиксированным размером, заданным при создании.
Объявление массива
package main
import "fmt"
func main() {
    var numbers [4]int // Массив из 4 целых чисел
    numbers[0] = 10
    numbers[1] = 20
    fmt.Println(numbers) // [10 20 0 0]
}
Короткий синтаксис
numbers := [4]int{10, 20, 30, 40}
fmt.Println(numbers) // [10 20 30 40]
Особенности
- Размер массива — часть его типа (
[4]int≠[5]int). - Элементы, не инициализированные явно, будут иметь стандартное значение (например, 
0дляint,""дляstring). 
⚠️ Массивы редко используются напрямую из-за фиксированного размера. Чаще применяются срезы.
🔪 Срезы (Slices)
Срезы — это динамические массивы, которые могут расти или уменьшаться. Они создаются на основе массива, но Go управляет их размером автоматически.
Создание среза
package main
import "fmt"
func main() {
    slice := []int{1, 2, 3} // Срез без фиксированного размера
    fmt.Println(slice)      // [1 2 3]
}
Добавление элементов
Функция append добавляет элементы в срез:
slice = append(slice, 4, 5)
fmt.Println(slice) // [1 2 3 4 5]
Длина и ёмкость
len(slice)— возвращает текущую длину (число элементов).cap(slice)— возвращает ёмкость (сколько элементов срез может вместить без перевыделения памяти).
fmt.Println(len(slice), cap(slice)) // 5 6
Как увеличивается ёмкость?
Когда срез заполняет свою ёмкость, Go выделяет новый массив с большей ёмкостью. Обычно ёмкость удваивается до определённого порога (примерно 1024 элемента), а затем растёт линейно (примерно на 25%). Это снижает количество перевыделений памяти.
Пример:
package main
import "fmt"
func main() {
    slice := make([]int, 0, 2) // Длина 0, ёмкость 2
    fmt.Printf("Начало: len=%d, cap=%d, %v\n", len(slice), cap(slice), slice)
    slice = append(slice, 1) // len=1, cap=2
    fmt.Printf("После 1: len=%d, cap=%d, %v\n", len(slice), cap(slice), slice)
    slice = append(slice, 2, 3) // len=3, cap=4 (ёмкость удвоилась)
    fmt.Printf("После 2,3: len=%d, cap=%d, %v\n", len(slice), cap(slice), slice)
}
Вывод:
Начало: len=0, cap=2, []
После 1: len=1, cap=2, [1]
После 2,3: len=3, cap=4, [1 2 3]
💡 Используйте
makeс начальной ёмкостью (make([]int, 0, n)), чтобы минимизировать перевыделение памяти.
🗺 Карты (Maps)
Карты — это словари, хранящие пары “ключ-значение”. Ключи уникальны, а типы ключей и значений задаются при создании.
Создание и работа с картой
package main
import "fmt"
func main() {
    scores := map[string]int{
        "Алиса": 90,
        "Боб":   85,
    }
    scores["Катя"] = 95 // Добавление
    delete(scores, "Боб") // Удаление
    fmt.Println(scores) // map[Алиса:90 Катя:95]
}
Проверка ключа
value, exists := scores["Алиса"]
if exists {
    fmt.Println("Оценка Алисы:", value) // Оценка Алисы: 90
} else {
    fmt.Println("Ключ не найден")
}
Итерация по карте
Используйте for ... range для перебора ключей и значений:
for name, score := range scores {
    fmt.Printf("%s: %d\n", name, score)
}
Вывод:
Алиса: 90
Катя: 95
⚠️ Порядок итерации по карте не гарантирован — Go специально рандомизирует его. Карта должна быть инициализирована (
makeили{}), иначе возникнет ошибкаnil map.
Пример: Подсчёт частоты
Карты отлично подходят для подсчёта данных, например, частоты символов в строке:
package main
import "fmt"
func main() {
    text := "hello"
    charCount := make(map[rune]int)
    for _, char := range text {
        charCount[char]++
    }
    fmt.Println(charCount) // map[e:1 h:1 l:2 o:1]
}
🏗 Структуры (Structs)
Структуры — это пользовательские типы, объединяющие поля для моделирования сложных объектов.
Объявление и использование
package main
import "fmt"
type Person struct {
    Name string
    Age  int
    Address struct { // Вложенная структура
        City  string
        Zip   string
    }
}
func main() {
    p := Person{
        Name: "Алиса",
        Age:  25,
        Address: struct {
            City string
            Zip  string
        }{City: "Москва", Zip: "101000"},
    }
    fmt.Println(p.Address.City) // Москва
}
Методы для структур
Структуры могут иметь методы — функции, привязанные к типу:
package main
import "fmt"
type Person struct {
    Name string
    Age  int
}
func (p Person) Greet() string {
    return "Привет, я " + p.Name + "!"
}
func main() {
    p := Person{Name: "Боб", Age: 30}
    fmt.Println(p.Greet()) // Привет, я Боб!
}
Анонимные структуры
Для временных данных можно использовать анонимные структуры:
package main
import "fmt"
func main() {
    point := struct {
        X, Y int
    }{X: 10, Y: 20}
    fmt.Println(point) // {10 20}
}
💡 Структуры подходят для моделирования сложных сущностей (например, пользователей, заказов). Методы добавляют поведение, делая структуры похожими на объекты.
📚 Полезные советы
- Массивы: Используйте для фиксированных данных (например, координаты).
 - Срезы: Задавайте начальную ёмкость через 
makeдля больших данных. - Карты: Проверяйте наличие ключа и используйте 
for ... rangeдля итерации. Инициализируйте карту перед использованием. - Структуры: Добавляйте методы для поведения и используйте вложенные структуры для сложных данных, но избегайте избыточной вложенности.
 - Оптимизация: Следите за 
capсрезов и очищайте карты с помощьюdeleteдля экономии памяти. 
🧠 Правильный выбор структуры данных упрощает код и повышает производительность.
🧪 Пример программы
package main
import "fmt"
func main() {
    // Массив
    arr := [3]int{10, 20, 30}
    fmt.Println("Массив:", arr)
    // Срез
    slice := make([]int, 0, 2)
    slice = append(slice, 1, 2, 3)
    fmt.Printf("Срез: %v, len=%d, cap=%d\n", slice, len(slice), cap(slice))
    // Карта
    grades := map[string]int{"Математика": 5, "Физика": 4}
    for subject, grade := range grades {
        fmt.Printf("Предмет: %s, оценка: %d\n", subject, grade)
    }
    // Структура
    type Student struct {
        Name   string
        Grades map[string]int
    }
    s := Student{Name: "Алиса", Grades: grades}
    fmt.Printf("Студент: %s, оценки: %v\n", s.Name, s.Grades)
}
🔍 Вопросы для самопроверки
- Чем отличается массив от среза в Go?
 - Как работает увеличение ёмкости среза при использовании 
append? - Как перебрать все элементы карты?
 - Как добавить метод к структуре?
 - Что выведет 
cap([]int{1, 2, 3})? - Как создать анонимную структуру и зачем она нужна?
 - Как удалить ключ из карты?
 
📌 Главное из главы
- Массивы — фиксированные наборы элементов одного типа.
 - Срезы — динамические массивы, ёмкость которых удваивается при переполнении (до ~1024 элементов).
 - Карты — словари для пар “ключ-значение”, поддерживающие итерацию и удаление ключей.
 - Структуры — пользовательские типы с полями и методами, включая вложенные и анонимные структуры.
 - Правильный выбор структуры данных улучшает читаемость и производительность.
 
🛠 Упражнение
Напишите программу, которая:
- Создаёт срез чисел 
{1, 2, 3}с начальной ёмкостью 2 и добавляет4, выводяlenиcap. - Создаёт карту для подсчёта частоты букв в строке 
"hello". - Создаёт структуру 
Bookс полямиTitle(строка),Pages(число) и методомInfo, возвращающим строку вида"Книга: <Title>, страниц: <Pages>". - Выводит результаты.