# 2.4 Структуры ## struct В Go мы можем определять новые типы контейнеров свойств или полей так же, как и в других языках программирования. Например, чтобы описать личность, мы можем создать тип `person`с полями "имя" и "возраст". Мы назовем этот тип `структурой(struct)`: ```Go type person struct { name string age int } ``` Вот так как легко определять `структуру`! У нас есть два поля: - `name` - `string`, используется для того, чтобы хранить имя человека. - `age` - `int`, используется для того, чтобы хранить возраст человека. Давайте посмотрим, как это использовать: ```Go type person struct { name string age int } var P person // p - переменная типа person P.name = "Astaxie" // присваиваем "Astaxie" полю 'name' переменной p P.age = 25 // присваиваем 25 полю 'age' переменной p fmt.Printf("Имя человека - %s\n", P.name) // получаем значение поля 'name' переменной p ``` Есть еще три способа определить `struct`: - Присвоить начальные значения по порядку: ```Go P := person{"Tom", 25} ``` - Использовать формат `поле:значение`, чтобы задать начальные значения полей структуры, при этом можно не соблюдать порядок, в котором поля шли при описании структуры: ```Go P := person{age: 24, name: "Bob"} ``` - Определить анонимную структуру, а затем задать ей значения: ```Go P := struct{name string; age int}{"Amy", 18} ``` Давайте рассмотрим конкретный пример: ```Go package main import "fmt" // Определяем новый тип type person struct { name string age int } // сравниваем возраст у двух людей, затем возвращаем возраст старшего из них и разницу в возрасте. // структуры передаются по значению func Older(p1, p2 person) (person, int) { if p1.age > p2.age { return p1, p1.age - p2.age } return p2, p2.age - p1.age } func main() { var tom person // задаем первоначальные значения tom.name, tom.age = "Tom", 18 // задаем значения в формате "поле:значение" bob := person{age: 25, name: "Bob"} // задаем значения в порядке, указанном при определении структуры paul := person{"Paul", 43} tb_Older, tb_diff := Older(tom, bob) tp_Older, tp_diff := Older(tom, paul) bp_Older, bp_diff := Older(bob, paul) fmt.Printf("Из %s и %s %s старше на %d лет\n", tom.name, bob.name, tb_Older.name, tb_diff) fmt.Printf("Из %s и %s %s старше на %d лет\n", tom.name, paul.name, tp_Older.name, tp_diff) fmt.Printf("Из %s и %s %s старше на %d лет\n", bob.name, paul.name, bp_Older.name, bp_diff) } ``` ### Встраиваемые поля в структуре Я только что показал Вам, как определять структуру с именами и типами полей. Но Go поддерживает и поля с типами, но без имен. Мы называем это встраиваемыми полями. Когда встраиваемое поле - структура, все поля этой структуры неявно становятся полями структуры, в которую оно встроено. Посмотрим на пример: ```Go package main import "fmt" type Human struct { name string age int weight int } type Student struct { Human // встраиваемое поле; это означает, что структура Student включает в себя все поля структуры Human. specialty string } func main() { // инициализируем студента mark := Student{Human{"Марк", 25, 120}, "Компьютерные науки"} // получаем доступ к полям fmt.Println("Его имя: ", mark.name) fmt.Println("Его возраст: ", mark.age) fmt.Println("Его масса: ", mark.weight) fmt.Println("Его специализация: ", mark.specialty) // изменяем значения полей mark.specialty = "Искусственный интеллект" fmt.Println("Марк поменял специализацию") fmt.Println("Его специализация: ", mark.specialty) // изменяем возраст fmt.Println("Марк постарел") mark.age = 46 fmt.Println("Его возраст: ", mark.age) // изменяем массу fmt.Println("Марк больше не атлет") mark.weight += 60 fmt.Println("Его масса: ", mark.weight) } ``` ![](images/2.4.student_struct.png?raw=true) Рисунок 2.7 Наследование в Student и Human Мы видим, что можно иметь доступ к значениям полей Student так же, как и к Human. Так работают встраиваемые поля. Очень круто, не так ли? Держитесь, есть кое-что покруче! Вы можете использовать Student, чтобы получить доступ к Human в этом встраиваемом поле! ```Go mark.Human = Human{"Маркус", 55, 220} mark.Human.age -= 1 ``` Все типы данных в Go могут быть использованы в качестве встраиваемых полей: ```Go package main import "fmt" type Skills []string type Human struct { name string age int weight int } type Student struct { Human // struct как встраиваемое поле Skills // срез из строк как встраиваемое поле int // встроенный тип как встраиваемое поле specialty string } func main() { // Инициализируем студента Джейн jane := Student{Human:Human{"Джейн", 35, 100}, specialty:"Биология"} // доступ к полям fmt.Println("Ее имя: ", jane.name) fmt.Println("Ее возраст: ", jane.age) fmt.Println("Ее масса: ", jane.weight) fmt.Println("Ее специализация: ", jane.specialty) // изменяем поле навыков jane.Skills = []string{"анатомия"} fmt.Println("Ее навыки: ", jane.Skills) fmt.Println("Она овладела еще двумя навыками: ") jane.Skills = append(jane.Skills, "физика", "golang") fmt.Println("Теперь ее навыки: ", jane.Skills) // изменяем встраиваемое поле jane.int = 3 fmt.Println("Ее любимое число: ", jane.int) } ``` В примере выше мы можем видеть, что данные всех типов могут быть встраиваемыми полями, и мы можем использовать функции, чтобы оперировать ими. Есть, впрочем, одна проблема. Если у `Human` есть поле под названием `phone`, а у `Student` тоже есть поле с таким именем, как нам быть? В Go есть простой способ решить эту задачу. Внешние поля имеют уровень доступа выше, что означает, что, обращаясь к `student.phone`, мы оперируем с полем `phone` в `student`,а не в `Human`. Это свойство проще представить как `перегрузку` полей. ```Go package main import "fmt" type Human struct { name string age int phone string // у Human есть поле phone } type Employee struct { Human // встраиваемое поле Human specialty string phone string // у Employee также появляется поле phone } func main() { Bob := Employee{Human{"Боб", 34, "777-444-XXXX"}, "Дизайнер", "333-222"} fmt.Println("Рабочий телефон Боба:", Bob.phone) // оперируем с поле phone в Human fmt.Println("Личный телефон Боба:", Bob.Human.phone) } ``` ## Ссылки - [Содержание](preface.md) - Предыдущий раздел: [Управляющие конструкции и функции](02.3.md) - Следующий раздел: [Объектно-ориентированное программирование](02.5.md)