Определение структур в Go

Построение абстракций на основе конкретных деталей – это лучшая возможность, которую язык программирования может дать разработчику. Структуры позволяют разработчикам Go описывать мир, в котором работает программа: то есть вместо строк, описывающих улицу, город или почтовый индекс, структуры позволяют нам говорить об адресе. Они служат естественным связующим звеном в документации и позволяют проинформировать других разработчиков (а заодно и для себя уточнить), какие данные важны для этой программы Go и как будущий код должен использовать эти данные. Структуры можно определять и использовать несколькими различными способами. В этом мануале мы рассмотрим каждый из этих методов.

Читайте также: Написание комментариев в Go

Определение структур

Структуры работают как бумажные формы, например, как в налоговой. Бумажные формы могут содержать поля для текстовой информации – это ваше имя и фамилия. Помимо текстовых полей, формы могут иметь флажки для обозначения логических значений (например, состоите ли вы в браке) или поля даты (чтоб указать дату рождения). Точно так же структуры собирают разные фрагменты данных и организуют их под разными именами полей. Инициализация переменной с новой структурой – по сути то же самое, что и копирование формы и подготовка ее к заполнению.

Чтобы создать новую структуру, вы должны сначала предоставить Go план, описывающий поля, которые содержит эта структура. Определение структуры обычно начинается с ключевого слова type, за которым следует имя структуры. После этого использется ключевое слово struct, за которым следует пара фигурных скобок {}, в них объявляются поля этой структуры. После того как вы определили структуру, вы можете объявить переменные, которые будут использовать это определение структуры. Например:

package main
import "fmt"
type Tutorial struct {
Name string
}
func main() {
c := Tutorial {
Name: "8host Blog",
}
fmt.Println(c.Name)
}

Когда вы запустите этот код, вы увидите такой вывод:

8host Blog

Сначала мы определяем структуру Tutorial, содержащую поле Name типа string. В теле main мы создаем экземпляр Tutorial, помещая пару фигурных скобок после имени типа (Tutorial), а затем определяем значения для полей этого экземпляра. Экземпляр c имеет свое имя. В рамках вызова функции fmt.Println мы извлекаем значения поля экземпляра, помещая точку после переменной, в которой был создан экземпляр; за ней следует имя поля, к которому мы хотели бы получить доступ. Например, c.Name в этом случае возвращает поле Name.

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

package main
import "fmt"
type Tutorial struct {
Name string
Type string
}
func main() {
c := Tutorial{"8host", "Blog"}
fmt.Println(c.Name, c.Type)
}

Вы получите вывод:

8host Blog

Мы добавили в Tutorial дополнительное поле, чтобы отслеживать Type в виде строки. При создании экземпляра Tutorial в теле main мы использовали более короткую форму объявления, предоставив значения для каждого поля по порядку и опустив их имена. В объявлении Tutorial{«8host», «Blog»} поле Name принимает значение 8host, а поле Type принимает значение Blog, потому что Name появляется первым в объявлении типа, а затем идет Type.

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

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

Экспорт полей структуры

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

package main
import "fmt"
type Tutorial struct {
Name string
Type string
password string
}
func main() {
c := Tutorial {
Name: "8host",
Type: "Blog",
password: "secret",
}
fmt.Println(c.Name, c.Type)
fmt.Println("Password is", c.password)
}

Вы получите такой вывод:

8host Blog
Password is secret

К нашим предыдущим примерам мы добавили новое поле, secret. Это неэкспортированное строковое поле – то есть любой другой пакет, который пытается создать экземпляр Creature, не сможет получить доступ к его полю secret или установить его значение. Но внутри одного пакета мы можем получить доступ к этим полям,. Поскольку метод main тоже находится в пакете main, он может ссылаться на c.password и извлекать сохраненное там значение.

Встроенные структуры

Помимо нового типа для представления структуры вы также можете определить встроенную структуру. Эти быстрые определения структур полезны в ситуациях, когда создание новых имен для типов структуры будет напрасной тратой усилий. Например, тесты часто используют структуру, чтобы определить все параметры, которые составляют конкретный тестовый пример. Было бы неудобно придумывать новые имена типа CreatureNamePrintingTestCase, раз эта структура используется только в одном месте.

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

package main
import "fmt"
func main() {
c := Tutorial {
Name string
Type string
}{
Name: "8host",
Type: "Blog",
}
fmt.Println(c.Name, c.Type)
}

Вывод:

8host Blog

Вместо того чтобы определять новый тип, описывающий структуру, с помощью ключевого слова type, мы определили встроенную структуру, поместив определение структуры сразу после оператора короткого присваивания, :=. Поля структуры определяются так же, как в предыдущих примерах, но затем нужно сразу предоставить другую пару фигурных скобок и значения, которые примет каждое поле. Используется эта структура так же, как и предыдущие – мы можем ссылаться на имена полей, используя точечную нотацию. Самое распространенное место использования встроенных структур – это тесты, для них часто определяются одноразовые структуры, содержащие данные и ожидания для конкретного примера.

Заключение

Структуры – это коллекции разнородных данных, определенных программистом для организации информации. Большинство программ имеют дело с огромными объемами данных, и без структур стало бы трудно запомнить, какие строковые или целочисленные переменные связаны, а какие – нет. В следующий раз, когда вы будете работать с группами переменных, попробуйте сгруппировать эти переменные, используя структуру: возможно, они все время описывали какую-то концепцию более высокого уровня.

Tags: ,