В этом учебнике мы узнаем о структурах.
Итак, struct
— это определяемый пользователем тип, который содержит набор именованных полей. По сути, он используется для группировки связанных данных в единое целое.
Если вы пришли из объектно-ориентированной среды, считайте, что структуры — это облегченные классы, которые поддерживают композицию, но не наследование.
Определение
Мы можем определить struct
следующим образом:
type Person struct {}
Мы используем ключевое слово type
для введения нового типа, затем имя, а затем ключевое слово struct
, чтобы указать, что мы определяем структуру.
Теперь давайте зададим ей несколько полей:
type Person struct {
FirstName string
LastName string
Age int
}
И если поля имеют одинаковый тип, мы можем их свернуть.
type Person struct {
FirstName, LastName string
Age int
}
Объявление и инициализация
Теперь, когда у нас есть наша структура, мы можем объявить ее так же, как и другие типы данных.
func main() {
var p1 Person
fmt.Println("Person 1:", p1)
}
$ go run main.go
Person 1: { 0}
Как мы видим, все поля struct инициализируются нулевыми значениями. Так FirstName
и LastName
установлены в ""
пустую строку, а Age
установлен в 0.
Мы также можем инициализировать его как «struct literal».
func main() {
var p1 Person
fmt.Println("Person 1:", p1)
var p2 = Person{FirstName: "Karan", LastName: "Pratap Singh", Age: 22}
fmt.Println("Person 2:", p2)
}
Для удобочитаемости мы можем разделить их новой строкой, но для этого также потребуется пробная запятая.
var p2 = Person{
FirstName: "Karan",
LastName: "Pratap Singh",
Age: 22,
}
$ go run main.go
Person 1: { 0}
Person 2: {Karan Pratap Singh 22}
Мы также можем инициализировать только подмножество полей.
func main() {
var p1 Person
fmt.Println("Person 1:", p1)
var p2 = Person{
FirstName: "Karan",
LastName: "Pratap Singh",
Age: 22,
}
fmt.Println("Person 2:", p2)
var p3 = Person{
FirstName: "Tony",
LastName: "Stark",
}
fmt.Println("Person 3:", p3)
}
$ go run main.go
Person 1: { 0}
Person 2: {Karan Pratap Singh 22}
Person 3: {Tony Stark 0}
Как мы видим, поле возраст человека 3 по умолчанию имеет нулевое значение.
Без имени поля
Go structs также поддерживает инициализацию без имен полей.
func main() {
var p1 Person
fmt.Println("Person 1:", p1)
var p2 = Person{
FirstName: "Karan",
LastName: "Pratap Singh",
Age: 22,
}
fmt.Println("Person 2:", p2)
var p3 = Person{
FirstName: "Tony",
LastName: "Stark",
}
fmt.Println("Person 3:", p3)
var p4 = Person{"Bruce", "Wayne"}
fmt.Println("Person 4:", p4)
}
Но вот в чем загвоздка, нам нужно будет указать все значения во время инициализации, иначе она завершится неудачей.
$ go run main.go
# command-line-arguments
./main.go:30:27: too few values in Person{...}
var p4 = Person{"Bruce", "Wayne", 40}
fmt.Println("Person 4:", p4)
Мы также можем объявить анонимную структуру.
func main() {
var p1 Person
fmt.Println("Person 1:", p1)
var p2 = Person{
FirstName: "Karan",
LastName: "Pratap Singh",
Age: 22,
}
fmt.Println("Person 2:", p2)
var p3 = Person{
FirstName: "Tony",
LastName: "Stark",
}
fmt.Println("Person 3:", p3)
var p4 = Person{"Bruce", "Wayne", 40}
fmt.Println("Person 4:", p4)
var a = struct {
Name string
}{"Golang"}
fmt.Println("Anonymous:", a)
}
Доступ к полям
Давайте немного почистим наш пример и посмотрим, как мы можем получить доступ к отдельным полям.
func main() {
var p = Person{
FirstName: "Karan",
LastName: "Pratap Singh",
Age: 22,
}
fmt.Println("FirstName", p.FirstName)
}
Мы также можем создавать указатели на структуры.
func main() {
var p = Person{
FirstName: "Karan",
LastName: "Pratap Singh",
Age: 22,
}
ptr := &p
fmt.Println((*ptr).FirstName)
fmt.Println(ptr.FirstName)
}
Оба утверждения равны, так как в Go нам не нужно явно разыменовывать указатель. Мы также можем использовать встроенную функцию new
.
func main() {
p := new(Person)
p.FirstName = "Karan"
p.LastName = "Pratap Singh"
p.Age = 22
fmt.Println("Person", p)
}
$ go run main.go
Person &{Karan Pratap Singh 22}
В качестве примечания: две структуры равны, если все их соответствующие поля также равны.
func main() {
var p1 = Person{"a", "b", 20}
var p2 = Person{"a", "b", 20}
fmt.Println(p1 == p2)
}
$ go run main.go
true
Экспортируемые поля
Теперь давайте узнаем, что такое экспортируемые и неэкспортируемые поля в struct. Как и правила для переменных и функций, если поле struct объявлено с идентификатором в нижнем регистре, оно не будет экспортировано и будет видно только тому пакету, в котором оно определено.
type Person struct {
FirstName, LastName string
Age int
zipCode string
}
Таким образом, поле zipCode
не будет экспортировано. То же самое относится и к структуре Person
, если мы переименуем ее в person
, она также не будет экспортирована.
type person struct {
FirstName, LastName string
Age int
zipCode string
}
Встраивание и композиция
Как мы уже говорили, Go не обязательно поддерживает наследование, но мы можем сделать нечто подобное с встраиванием.
type Person struct {
FirstName, LastName string
Age int
}
type SuperHero struct {
Person
Alias string
}
Итак, наша новая структура будет иметь все свойства исходной структуры. И она должна вести себя так же, как наша обычная структура.
func main() {
s := SuperHero{}
s.FirstName = "Bruce"
s.LastName = "Wayne"
s.Age = 40
s.Alias = "batman"
fmt.Println(s)
}
$ go run main.go
{{Bruce Wayne 40} batman}
Однако обычно это не рекомендуется, и в большинстве случаев предпочтительнее композиция. Поэтому вместо встраивания мы просто определим его как обычное поле.
type Person struct {
FirstName, LastName string
Age int
}
type SuperHero struct {
Person Person
Alias string
}
Следовательно, мы можем переписать наш пример и с композицией.
func main() {
p := Person{"Bruce", "Wayne", 40}
s := SuperHero{p, "batman"}
fmt.Println(s)
}
$ go run main.go
{{Bruce Wayne 40} batman}
Опять же, здесь нет правильного или неправильного, но, тем не менее, встраивание иногда бывает полезным.
Теги структур
Тег struct — это просто тег, который позволяет нам прикрепить к полю информацию метаданных, которая может быть использована для пользовательского поведения с помощью пакета reflect
.
Давайте узнаем, как мы можем определять теги struct.
type Animal struct {
Name string `key:"value1"`
Age int `key:"value2"`
}
Теги часто встречаются в пакетах кодирования, таких как XML, JSON, YAML, ORMs и управление конфигурацией.
Вот пример тегов для кодировщика JSON.
type Animal struct {
Name string `json:"name"`
Age int `json:"age"`
}
Свойства
Наконец, давайте обсудим свойства структур.
Структуры — это типы значений. Когда мы присваиваем одну переменную struct
другой, создается и присваивается новая копия struct
.
Аналогично, когда мы передаем struct
другой функции, функция получает свою собственную копию struct
.
package main
import "fmt"
type Point struct {
X, Y float64
}
func main() {
p1 := Point{1, 2}
p2 := p1 // Copy of p1 is assigned to p2
p2.X = 2
fmt.Println(p1) // Output: {1 2}
fmt.Println(p2) // Output: {2 2}
}
Пустая структура занимает ноль байт памяти.
package main
import (
"fmt"
"unsafe"
)
func main() {
var s struct{}
fmt.Println(unsafe.Sizeof(s)) // Output: 0
}
Эта статья является частью моего открытого курса по Go, доступного на Github.
karanpratapsingh / go-course
Освойте основы и расширенные возможности языка программирования Go
Курс по Go
Привет, добро пожаловать на курс, и спасибо за изучение Go. Я надеюсь, что этот курс обеспечит вам отличный опыт обучения.
Этот курс также доступен на моем сайте, а также на Educative.io
Оглавление
-
Начало работы
- Что такое Go?
- Зачем изучать Go?
- Установка и настройка
-
Глава I
- Hello World
- Переменные и типы данных
- Форматирование строк
- Управление потоком данных
- Функции
- Модули
- Рабочие пространства
- Пакеты
- Полезные команды
- Сборка
-
Глава II
- Указатели
- Структуры
- Методы
- Массивы и фрагменты
- Карты
-
Глава III
- Интерфейсы
- Ошибки
- Паника и восстановление
- Тестирование
- Дженерики
-
Глава IV
- Параллелизм
- Гороутины
- Каналы
- Выбрать
- Пакет синхронизации
- Расширенные шаблоны параллелизма
- Контекст
-
Приложение
- Следующие шаги
- Ссылки
Что такое Go?
Go (также известный как Golang) — это язык программирования, разработанный в Google в 2007 году и открытый в 2009 году.
Основное внимание в нем уделяется простоте, надежности и эффективности. Он был разработан, чтобы объединить эффективность, скорость и безопасность статически типизированного и компилируемого языка с легкостью…