После изучения данного урока вы сможете:

  • Представить координаты Марса в виде структуры;
  • Закодировать структуры в популярный формат данных JSON.

Транспортные средства состоят из множества деталей, у которых могут быть свои значения (или состояние). Двигатель запущен, колеса крутятся, бензина достаточно. Использование отдельных переменных для каждого значения сродни разобранному на складе магазина транспорту. В здании могут быть открытые окна и двери, осталось собрать. Для сбора деталей или создания структуры в Go используется тип structure.

Форум Гоферов

Мы работаем над форумом для программистов на Golang. Очень нужны модераторы которые хотят помочь с ответами для новичков и помочь в развитии Go-сообщества.

Go на Форум

Уроки, статьи и Видео

Мы публикуем в паблике ВК и Telegram качественные обучающие материалы для быстрого изучения Go. Подпишитесь на нас в ВК и в Telegram. Поддержите сообщество Go программистов.

Go в ВК Go в Telegram

Содержание статьи

В то время как в коллекции используется один тип, структуры дают возможность группировать элементы разных типов. Осмотритесь вокруг. Какие из ближайших к вам предметов можно представить как структуру?

Объявление структуры в Golang

Пара координат станет хорошим примером для разбора принципа работы структуры. Широта и долгота всегда используются вместе. Функция для подсчета расстояния между двумя локациями потребуют две пары координат:

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

Структура curiosity в следующем примере объявляется с полями вещественного типа для широты и долготы. Для присваивания значения полю или получения к нему доступа используется запись с точкой, то есть указывается название переменной, затем точка и в конце название поля. Пример показан ниже.

На заметку: Функции семейства Print отобразят содержимое структур за вас.

Марсоход Curiosity начал свое путешествие в кратере Брэдбери, координаты которого 4°35’22.2” S, 137°26’30.1” E. В Листинге 1 широта и долгота для кратера Брэдбери представлена в десятичных значениях с положительной северной шириной и положительной восточной долготой, как показано на Фигуре 1.

структура golang

Фигура 1: Широта и долгота в десятичных значениях

Вопросы для проверки:

  1. В чем преимущество структур в сравнении с отдельными переменными?
  2. Кратер Брэдбери расположен на 4400 метра ниже марсианского «уровня моря». Если бы у curiosity было поле высоты уровня моря, как можно было бы присвоить ему значение -4400.

Повторное использование структур с типами в Go

Если вам нужно несколько структур с одинаковыми полями, вы можете определить тип наподобие того, как мы это делали с типом celsius в уроке о методах. Тип location, объявленный в следующем примере, используется для помещения марсохода Спирит на Мемориальной Станции шаттла Колумбия и марсохода Оппортьюнити на Мемориальной Станции Челленджера.

Вопрос для проверки:

Как можно адаптировать код из Листинга 1 для использования типа location для марсохода Curiosity в кратере Брэдбери?

Инициализация структур через композитные литералы Golang

Для инициализации структур композитные литералы используются в двух различных формах. В Листинге 3 переменные opportunity и insight инициализируются через использование пар «поле-значение». Поля могут значиться в любом порядке, а поля, что не в списке, сохранят нулевое значение для своего типа. Данная форма принимает изменения и продолжит корректную работу даже при добавлении в структуру полей или при изменении порядка полей. Если location достигает поля высоты уровня моря, то opportunity и insight принимают по умолчанию нулевое значение высоты уровня моря.

Композитный литерал в Листинге 4 не уточняет названия полей. Вместо этого значение для каждого поля должно предоставляться в том же порядке, в котором они указаны в определении структуры. Данная форма лучше всего подходит для стабильных типов, у которых только несколько полей. Если тип location достигает поля высоты уровня моря, spirit должен уточнить значение высоты уровня моря для компиляции программы. Смешивание порядка lat и long вызовет ошибки компиляции, однако программа не сможет вывести правильные результаты.

Неважно, как вы инициализируете структуру, вы можете модифицировать специальный символ %v со знаком плюса + для вывода названий полей, как показано в следующем примере. Это особенно полезно для крупных структур.

Вопрос для проверки: 

В чем преимущества синтаксиса композитного литерала с «полем-значением» перед формой «только со значениями»?

Копирование структур

Когда марсоход достигает востока кратера Брэдбери, бухты Йеллоунайф, место кратера Брэдбери в реальной жизни не меняется, как и в следующем листинге. Переменная curiosity инициализируется с копией значений, находящихся в bradbury, поэтому изменения происходят независимо.

Вопрос для проверки:

При передачи curiosity функции, что манипулирует lat или long, будут ли изменения видны в месте вызова?

Пример среза структур в Golang

Срез структур []struct представляет собой коллекцию нуля или более значений (срез), где каждое значение базируется на структуре вместо примитивного типа вроде float64.

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

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

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

Вопрос для проверки:

В чем опасность использования нескольких связанных между собой срезов?

Кодирование структур в JSON

JavaScript Object Notation, или JSON является стандартным форматом данных, введенным Дугласом Крокфордом. Он основан на подмножестве языка JavaScript, а также поддерживается другими языками программирования. JSON широко используется для веб API (Application Programming Interfaces), включая MAAS API, что предоставляет данные о погоде от марсохода Curiosity.

Функция Marshal из пакета json используется в Листинге 9 для кодирования данных из location в формат JSON. Marshal возвращает данные JSON в виде байтов, что можно отправить или конвертировать в строки для отображения на экране. В результате также может выйти ошибка. Об этом поговорим в одном из следующих уроков.

Обратите внимание, что ключи JSON совпадают с названиями полей структуры location. Для работы, пакет json требует экспорта полей. Если Lat и Long начинаются с маленькой буквы, выводом будет {}.

Вопрос для проверки: 

Как расшифровывается аббревиатура JSON?

Изменение JSON через теги struct в Golang

В Go, пакет json требует, чтобы первые буквы полей были в верхнем регистре, то же самое касается составных названий в стиле CamelCase. Возможно, вам понадобятся ключи JSON в  snake_case стиле, особенно при взаимодействии с Python или Ruby. Поля структуры могут быть помечены тегами с названиями полей, которые бы вы хотели, чтобы использовал пакет json.

Единственное изменение между Листингом 9 и Листингом 10 в использовании тегов struct, что изменяют вывод функции Marshal. Обратите внимание, что поля Lat и Long все еще должны экспортироваться, чтобы пакет json увидел их.

Теги структур являются обыкновенными строками, что связаны с полями структуры. Предпочтительны литералы необработанной строки () , потому что кавычкам не нужно экранировать через обратный слеш, как в случае с менее читабельным "json:\"latitude\"".

Теги структуры форматируются как key:"value", где ключом обычно является название пакета. Для настройки поля Lat для JSON и XML тегом структуры был бы json:"latitude" xml:"latitude".

Как предполагает название, теги struct нужны только для полей структур, хотя json.Marshal закодирует и другие типы.

Вопрос для проверки:

Почему поля Lat и Long начинаются с букв в верхнем регистре при кодировании JSON?

Заключение

  • Структуры группируют значения вместе в одну единицу;
  • Структуры являются значениями, что копируются во время присваивания или передачи функциям;
  • Композитные литералы предоставляют удобные средства для инициализации структур;
  • Теги struct декорируют экспортируемые поля с дополнительными информацией, что пакеты могут использовать;
  • Пакет json использует теги в struct для контролирования вывода названий полей.

Итоговое задание для проверки: 

Напишите программу для отображения закодированных данных в JSON трех мест высадки марсохода в Листинге 8. JSON должен содержать название каждого места высадки и использовать теги структуры, как показано в Листинге 10.

Чтобы сделать вывод приятнее, используйте функцию MarshalIndent из пакета json.