Как можно создать переменную структуры golang
Структуры представляют тип данных, определяемый разработчиком и служащий для представления каких-либо объектов. Структуры содержат набор полей, которые представляют различные атрибуты объекта. Для определения структуры применяются ключевые слова type и struct :
type имя_структуры struct
Каждое поле имеет название и тип данных, как переменная. Например, определим структуру, которая представляет человека:
type person struct
Структура называется person. Она имеет два поля: name (имя человека, представляет тип string) и age (возраст человека, представляет тип int).
Создание и инициализация структуры
Структура представляет новый тип данных, и мы можем определить переменную данного типа:
var tom person
С помощью инициализатора можно передать структуре начальные значения:
var tom person = person
Инициализатор представляет набор значений в фигурных скобках. Причем эти значения передаются полям структуры в том порядке, в котором поля определены в структуре. Например, в данном случае строка «Tom» передается первому полю — name, а второе значение — 23 передается второму полю — age.
Также мы можем явным образом указать какие значения передаются свойствам:
var alice person = person
Также можно использовать сокращенные способы инициализации переменной структуры:
var tom = person bob := person
Можно даже не указывать никаких значений, в этом случае свойства структуры получат значения по умолчанию:
undefined := person <> // name - пустая строка, age - 0
Обращение к полям структуры
Для обращения к полям структуры после переменной ставится точка и указывается поле структуры:
package main import "fmt" type person struct < name string age int >func main() < var tom = person fmt.Println(tom.name) // Tom fmt.Println(tom.age) // 24 tom.age = 38 // изменяем значение fmt.Println(tom.name, tom.age) // Tom 38 >
Указатели на структуры
Как и в случае с обычными переменнами, можно создавать указатели на структуры.
package main import "fmt" type person struct < name string age int >func main() < tom := person var tomPointer *person = &tom tomPointer.age = 29 fmt.Println(tom.age) // 29 (*tomPointer).age = 32 fmt.Println(tom.age) // 32 >
Для инициализации указателя на структуру необязательно присваивать ему адрес переменной. Можно присвоить адрес безымянного объекта следующим образом:
var tom *person = &person var bob *person = new(person)
Для обращения к полям структуры через указатель можно использовать операцию разыменования ( (*tomPointer).age ), либо напрямую обращаться по указателю ( tomPointer.age ).
Также можно определять указатели на отдельные поля структуры:
tom := person var agePointer *int = &tom.age // указатель на поле tom.age *agePointer = 35 // изменяем значение поля fmt.Println(tom.age) // 35
Структуры и интерфейсы
Несмотря на то, что вполне можно писать программы на Go используя только встроенные типы, в какой-то момент это станет очень утомительным занятием. Вот пример — программа, которая взаимодействует с фигурами:
package main import ("fmt"; "math") func distance(x1, y1, x2, y2 float64) float64 < a := x2 - x1 b := y2 - y1 return math.Sqrt(a*a + b*b) >func rectangleArea(x1, y1, x2, y2 float64) float64 < l := distance(x1, y1, x1, y2) w := distance(x1, y1, x2, y1) return l * w >func circleArea(x, y, r float64) float64 < return math.Pi * r*r >func main()
Отслеживание всех переменных мешает нам понять, что делает программа, и наверняка приведет к ошибкам.
Структуры
С помощью структур эту программу можно сделать гораздо лучше. Структура — это тип, содержащий именованные поля. Например, мы можем представить круг таким образом:
type Circle struct
Ключевое слово type вводит новый тип. За ним следует имя нового типа ( Circle ) и ключевое слово struct , которое говорит, что мы определяем структуру и список полей внутри фигурных скобок. Каждое поле имеет имя и тип. Как и с функциями, мы можем объединять поля одного типа:
type Circle struct
Инициализация
Мы можем создать экземпляр нового типа Circle несколькими способами:
var c Circle
Подобно другим типами данных, будет создана локальная переменная типа Circle , чьи поля по умолчанию будут равны нулю ( 0 для int , 0.0 для float , «» для string , nil для указателей, …). Также, для создания экземпляра можно использовать функцию new .
c := new(Circle)
Это выделит память для всех полей, присвоит каждому из них нулевое значение и вернет указатель ( *Circle ). Часто, при создании структуры мы хотим присвоить полям структуры какие-нибудь значения. Существует два способа сделать это. Первый способ:
c := Circle
Второй способ — мы можем опустить имена полей, если мы знаем порядок в котором они определены:
c := Circle
Поля
Получить доступ к полям можно с помощью оператора . (точка):
fmt.Println(c.x, c.y, c.r) c.x = 10 c.y = 5
Давайте изменим функцию circleArea так, чтобы она использовала структуру Circle :
func circleArea(c Circle) float64
В функции main у нас будет:
c := Circle fmt.Println(circleArea(c))
Очень важно помнить о том, что аргументы в Go всегда копируются. Если мы попытаемся изменить любое поле в функции circleArea , оригинальная переменная не изменится. Именно поэтому мы будем писать функции так:
func circleArea(c *Circle) float64
c := Circle fmt.Println(circleArea(&c))
Методы
Несмотря на то, что программа стала лучше, мы все еще можем значительно её улучшить, используя метод — функцию особого типа:
func (c *Circle) area() float64
Между ключевым словом func и именем функции мы добавили «получателя». Получатель похож на параметр — у него есть имя и тип, но объявление функции таким способом позволяет нам вызывать функцию с помощью оператора . :
fmt.Println(c.area())
Это гораздо проще прочесть, нам не нужно использовать оператор & (Go автоматически предоставляет доступ к указателю на Circle для этого метода), и поскольку эта функция может быть использована только для Circle мы можем назвать её просто area .
Давайте сделаем то же самое с прямоугольником:
type Rectangle struct < x1, y1, x2, y2 float64 >func (r *Rectangle) area() float64
В main будет написано:
r := Rectangle fmt.Println(r.area())
Встраиваемые типы
Обычно, поля структур представляют отношения принадлежности (включения). Например, у Circle (круга) есть radius (радиус). Предположим, у нас есть структура Person (личность):
type Person struct < Name string >func (p *Person) Talk()
И если мы хотим создать новую структуру Android , то можем сделать так:
type Android struct
Это будет работать, но мы можем захотеть создать другое отношение. Сейчас у андроида «есть» личность, можем ли мы описать отношение андроид «является» личностью? Go поддерживает подобные отношения с помощью встраиваемых типов, также называемых анонимными полями. Выглядят они так:
type Android struct
Мы использовали тип ( Person ) и не написали его имя. Объявленная таким способом структура доступна через имя типа:
a := new(Android) a.Person.Talk()
Но мы также можем вызвать любой метод Person прямо из Android :
a := new(Android) a.Talk()
Это отношение работает достаточно интуитивно: личности могут говорить, андроид это личность, значит андроид может говорить.
Интерфейсы
Вы могли заметить, что названия методов для вычисления площади круга и прямоугольника совпадают. Это было сделано не случайно. И в реальной жизни и в программировании отношения могут быть очень похожими. В Go есть способ сделать эти случайные сходства явными с помощью типа называемого интерфейсом. Пример интерфейса для фигуры ( Shape ):
type Shape interface
Как и структуры, интерфейсы создаются с помощью ключевого слова type , за которым следует имя интерфейса и ключевое слово interface . Однако, вместо того, чтобы определять поля, мы определяем «множество методов». Множество методов — это список методов, которые будут использоваться для «реализации» интерфейса.
В нашем случае у Rectangle и Circle есть метод area , который возвращает float64 , получается они оба реализуют интерфейс Shape . Само по себе это не очень полезно, но мы можем использовать интерфейсы как аргументы в функциях:
func totalArea(shapes . Shape) float64 < var area float64 for _, s := range shapes < area += s.area() >return area >
Мы будем вызывать эту функцию так:
fmt.Println(totalArea(&c, &r))
Интерфейсы также могут быть использованы в качестве полей:
type MultiShape struct
Мы можем даже хранить в MultiShape данные Shape , определив в ней метод area :
func (m *MultiShape) area() float64 < var area float64 for _, s := range m.shapes < area += s.area() >return area >
Теперь MultiShape может содержать Circle , Rectangle и даже другие MultiShape .
Задачи
- Какая разница между методом и функцией?
- В каких случаях могут пригодиться встроенные (скрытые) поля?
- Добавьте новый метод perimeter в интерфейс Shape , который будет вычислять периметр фигуры. Имплементируйте этот метод для Circle и Rectangle .
Go: Структуры
В Go нет классов и привычной реализации ООП. Вместо классов в языке используются структуры — наборы полей, имеющих название и тип данных. Объявление структуры имеет следующий вид:
type Person struct < // [название поля] [тип данных] Name string Age int >func main() < p := Personp.Name // "John" p.Age // 25 >
Структуру можно инициализировать, не передавая значения. В этом случае каждое поле примет свое «нулевое» значение:
func main() < p := Person<>p.Name // "" p.Age // 0 >
Регистр первой буквы в названии структуры и полей означает публичность точно так же, как в переменных и функциях. Если первая буква заглавная, то структуру можно инициализировать во внешних пакетах. В противном случае она доступна только в рамках текущего пакета:
type Person struct < // структура публична Name string // поле публично wallet wallet // поле приватно: можно обращаться только внутри текущего пакета >type wallet struct < // структура приватна: можно инициализировать только внутри текущего пакета id string moneyAmount float64 >
У любого поля структуры можно указать теги. Они используются для метаинформации о поле для сериализации, валидации, маппинга данных из БД и тд. Тег указывается после типа данных через бектики:
type User struct
Тег json используется для названий полей при сериализации/десериализации структуры в json и обратно:
package main import ( "encoding/json" "fmt" ) type User struct < ID int64 `json:"id"` Email string `json:"email"` FirstName string `json:"first_name"` >func main() < u := User<>u.ID = 22 u.Email = "test@test.com" u.FirstName = "John" bs, _ := json.Marshal(u) fmt.Println(string(bs)) // >
Тег validate используется Go-валидатором. В следующем примере присутствует вызов функции у структуры v.Struct(u) . Функции структур — методы — мы разберем подробно в соответствующем уроке, а пока просто посмотрите, как происходит вызов:
package main import ( "fmt" "github.com/go-playground/validator/v10" ) type User struct < ID int64 `validate:"required"` Email string `validate:"required,email"` FirstName string `validate:"required"` >func main() < // создали пустую структуру, чтобы проверить валидацию u := User<>// создаем валидатор v := validator.New() // метод Struct валидирует переданную структуру и возвращает ошибку `error`, если какое-то поле некорректно fmt.Println(v.Struct(u)) >
Key: 'User.ID' Error:Field validation for 'ID' failed on the 'required' tag Key: 'User.Email' Error:Field validation for 'Email' failed on the 'required' tag Key: 'User.FirstName' Error:Field validation for 'FirstName' failed on the 'required' tag
Задание
На сервер приходит HTTP-запрос. Тело запроса парсится и мапится в модель. Сразу работать с моделью нельзя, потому что данные могут быть неверными.
Реализуйте функцию Validate(req UserCreateRequest) string , которая валидирует запрос и возвращает строку с ошибкой «invalid request», если модель невалидна. Если запрос корректный, то функция возвращает пустую строку. Правила валидации и структура модели описаны ниже. Не используйте готовые библиотеки и опишите правила самостоятельно.
type UserCreateRequest struct < FirstName string // не может быть пустым; не может содержать пробелы Age int // не может быть 0 или отрицательным; не может быть больше 150 >
Наличие пробелов можно проверить с помощью функции strings.Contains(firstName, » «) .
Упражнение не проходит проверку — что делать?
Если вы зашли в тупик, то самое время задать вопрос в «Обсуждениях». Как правильно задать вопрос:
- Обязательно приложите вывод тестов, без него практически невозможно понять что не так, даже если вы покажете свой код. Программисты плохо исполняют код в голове, но по полученной ошибке почти всегда понятно, куда смотреть.
В моей среде код работает, а здесь нет
Тесты устроены таким образом, что они проверяют решение разными способами и на разных данных. Часто решение работает с одними входными данными, но не работает с другими. Чтобы разобраться с этим моментом, изучите вкладку «Тесты» и внимательно посмотрите на вывод ошибок, в котором есть подсказки.
Мой код отличается от решения учителя
Это нормально , в программировании одну задачу можно выполнить множеством способов. Если ваш код прошел проверку, то он соответствует условиям задачи.
В редких случаях бывает, что решение подогнано под тесты, но это видно сразу.
Прочитал урок — ничего не понятно
Создавать обучающие материалы, понятные для всех без исключения, довольно сложно. Мы очень стараемся, но всегда есть что улучшать. Если вы встретили материал, который вам непонятен, опишите проблему в «Обсуждениях». Идеально, если вы сформулируете непонятные моменты в виде вопросов. Обычно нам нужно несколько дней для внесения правок.
Кстати, вы тоже можете участвовать в улучшении курсов: внизу есть ссылка на исходный код уроков, который можно править прямо из браузера.
Полезное
#3 – Создание структур (модели данных)

Язык Go не содержит типа данных «class». Вместо него используются структуры. За урок мы научимся работать со структурами и научимся создавать объекты на их основе.
Видеоурок
Структуры в Golang
В языке Go нет отдельного типа данных для создания классов. Вместо классов в Golang используются структуры данных. На основе структуры вы можете создать объект, добавить к нему поля, методы, реализовать наследование и полиморфизм.
Создание структур
Для создания структуры необходимо прописать ключевое слово type и далее название. Общепринято начинать названия структур с буквы в верхнем регистре, но если этого не сделать, то ошибки не будет.
В любой структуре можно создавать поля (переменные), методы (функции), а также конструкторы.
Создав новую структуру и поместив туда какую-либо информацию мы можем создавать на ее основе новые объекты. Объекты будут иметь доступ ко всем характеристикам.
Пример приведен ниже:
type Book struct < Name string // Структура с одним полем >
На основе такой структуры мы можем создать множество объектов. Каждый объект в данном случае будет представлять из себя конкретную книжку. Для каждого объекта мы можем указать уникальные данные.
Чтобы создать объект нам потребуется следующий код:
obj_new := Some<> // Создание объекта obj_second := Some // Создание 2 объекта
Весь код будет доступен после подписки на проект!