Зарегистрируйтесь, чтобы продолжить обучение

Что такое структура и зачем она нужна Структуры в Go

В языке Go одним из ключевых инструментов для работы с данными являются структуры. Если переменные позволяют хранить отдельные значения (числа, строки, булевы значения), а массивы и срезы — наборы однотипных данных, то структуры дают возможность объединять в одном месте разнородные данные, которые логически связаны между собой.

Структуры можно представить как собственные типы данных, которые мы конструируем под задачу. Они помогают упорядочивать код и делать его более читаемым и удобным в поддержке.

Пример

Представим, что нам нужно описать студента: у него есть имя, возраст, список курсов и средний балл. Если бы мы хранили все в отдельных переменных, получилось бы громоздко:

var name string
var age int
var courses []string
var gpa float64

При этом сразу видно, что все эти переменные относятся к одному объекту — студенту. Гораздо удобнее объединить их в одну структуру:

type Student struct {
    Name    string
    Age     int
    Courses []string
    GPA     float64
}

Теперь у нас появился собственный тип Student, который можно использовать в программе как единый объект.

Зачем нужны структуры

  1. Группировка данных — все, что относится к одному объекту, хранится вместе.
  2. Улучшение читаемости — названия полей отражают смысл данных.
  3. Основа для объектного подхода в Go — к структурам можно добавлять методы.
  4. Удобство работы с внешними данными — например, JSON удобно парсить в структуры.

Пример

Создадим структуру Book и используем ее в коде:

package main

import "fmt"

type Book struct {
    Title string
    Author string
    Pages int
}

func main() {
    book := Book{
        Title: "Война и мир",
        Author: "Лев Толстой",
        Pages: 1225,
    }

    fmt.Println(book.Title, "—", book.Author, "(", book.Pages, "стр.)")
}

Результат:

Война и мир — Лев Толстой (1225 стр.)

Структуры в Go — это способ объединить разные данные в один логический объект. Они позволяют описывать сущности реального мира (пользователь, книга, заказ, транзакция) и делают программы понятнее.

Как без структур

Представим, что мы описываем систему заказов. Каждый заказ должен содержать идентификатор, имя клиента, список товаров и статус. Если мы не используем структуры, нам приходится хранить данные в отдельных срезах и синхронизировать их по индексам:

func main() {
    // данные о заказах разнесены по разным срезам
    orderIDs := []int{101, 102}
    customers := []string{"Иван", "Мария"}
    items := [][]string{
        {"Ноутбук", "Мышь"},
        {"Телефон"},
    }

    statuses := []string{"new", "paid"}

    // чтобы вывести заказ, приходится помнить про индексы
    fmt.Println("Заказ:", orderIDs[0],
        "Клиент:", customers[0],
        "Товары:", items[0],
        "Статус:", statuses[0])
}

Результат:

Заказ: 101 Клиент: Иван Товары: [Ноутбук Мышь] Статус: new

Проблемы такого подхода:

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

Как со структурами

Теперь опишем заказ как единый объект:

// Order описывает заказ
type Order struct {
    ID int
    Customer string
    Items []string
    Status string
}

func main() {
    // создаем объект заказа
    order := Order{
        ID: 101,
        Customer: "Иван",
        Items: []string{"Ноутбук", "Мышь"},
        Status: "new",
    }

    // обращаемся к полям напрямую
    fmt.Println("Заказ:", order.ID,
        "Клиент:", order.Customer,
        "Товары:", order.Items,
        "Статус:", order.Status)
}

Теперь данные связаны, ошибки с индексами мы избегаем, а при добавлении нового поля (Amount, CreatedAt) нам достаточно изменить определение структуры.

Объекты и структуры в Go

В классических объектно-ориентированных языках есть классы и объекты. В Go нет классов и наследования, но структуры позволяют описывать данные и методы для них. Экземпляр структуры можно называть объектом в широком смысле — это значение пользовательского типа, объединяющее поля. Однако формально это просто значение структуры, а не объект ООП.

Синтаксис структур и базовые практики

В Go для объявления структур используется ключевое слово struct. Оно всегда пишется после имени нового типа и открывает блок с перечнем полей. Этот синтаксис уникален для Go: структуры не объявляются через ключевые слова вроде class или record, как в других языках.

Синтаксис объявления

// общий шаблон

type <ИмяСтруктуры> struct {
Поле1 Тип1
Поле2 Тип2
}
  • type создает новый тип.
  • Имя структуры принято писать с заглавной буквы.
  • Внутри фигурных скобок перечисляются поля и их типы.

Создание экземпляра

  • Литерал структуры: u := User{Login: "alex", Password: "pwd"}
  • Пустая структура: var u User
  • Через new: u := new(User) (возвращает указатель).

Обращение к полям

func main() {
    u := User{Login: "alex"}
    fmt.Println(u.Login) // чтение
    u.Login = "alexander" // запись
}

Лучше всего придерживаться следующих правил,

  • Имя структуры отражает сущность (User, Order, Report).
  • Поля называются понятно и однозначно (CreatedAt, Status, Amount).
  • Экспортируемые поля (с заглавной буквы) видны за пределами пакета, неэкспортируемые (с маленькой) — только внутри.
  • Логически связанные поля лучше группировать во вложенные структуры.

Итоги

Структуры — пользовательские типы данных в Go. Они объединяют разнородные поля в один объект; позволяют описывать сущности предметной области; дают именованный доступ к данным; упрощают изменение модели (добавление/удаление полей происходит в одном месте). На следующих уроках мы разберем поля и их типы, указатели на структуры, методы, конструкторы, композицию, теги/JSON и сравнение структур.


Самостоятельная работа

Структуры позволяют объединять разнородные данные в один логический объект. В этой работе опишите простые сущности и поработайте с их полями — это закрепит базовый синтаксис и логику использования структур.

Фильм

Вспомните любимый фильм и опишите его «паспорт» в виде структуры Movie со следующими полями:

  • Название — строка
  • Год выхода — целое число
  • Жанры — список строк
  • Рейтинг — число с плавающей точкой

Создайте один экземпляр и выведите строку в формате:

Название (год) — жанры: [...] — рейтинг: ...
Показать пример ответа
package main

import (
    "fmt"
)

type Movie struct {
    Title  string
    Year   int
    Genres []string
    Rating float64
}

func main() {
    m := Movie{
        Title:  "Интерстеллар",
        Year:   2014,
        Genres: []string{"Sci‑Fi", "Drama"},
        Rating: 8.6,
    }

    fmt.Printf("%s (%d) — жанры: %v — рейтинг: %.1f\n", m.Title, m.Year, m.Genres, m.Rating)
}

Банковский счёт

Добавим немного практики: опишите структуру Account со следующими полями:

  • Идентификатор — целое число
  • Владелец — строка
  • Баланс — число с плавающей точкой

Напишите функцию Deposit(acc *Account, amount float64), которая аккуратно пополняет счёт на указанную сумму. Для наглядности выведите баланс «до» и «после» вызова функции.

Показать пример ответа
package main

import (
    "fmt"
)

type Account struct {
    ID      int
    Owner   string
    Balance float64
}

// Deposit увеличивает баланс на amount.
func Deposit(acc *Account, amount float64) {
    if amount <= 0 {
        return // в базовом уроке просто игнорируем невалидные суммы
    }
    acc.Balance += amount
}

func main() {
    a := Account{ID: 1, Owner: "Иван", Balance: 1000}
    fmt.Println("До:", a.Balance)
    Deposit(&a, 500)
    fmt.Println("После:", a.Balance)
}

Анализ

Пара минут на размышления: почему хранить связанные данные в отдельных срезах (например, ids, owners, balances) — плохая идея? Сформулируйте как минимум два аргумента (ошибки из‑за индексов, сложности с добавлением новых полей и т.п.).

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

Дополнительные материалы

  1. Tour of Go — Structs
  2. Effective Go — Structs
  3. Go Spec — Struct types

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff