Go
Структуры
Поиск…
Вступление
Структуры представляют собой совокупности различных переменных, упакованных вместе. Сама структура - это всего лишь пакет, содержащий переменные и обеспечивающий их доступность.
В отличие от C, структуры Go могут иметь приложенные к ним методы. Он также позволяет им реализовывать интерфейсы. Это делает структуры Go похожими на объекты, но они (возможно, намеренно) пропускают некоторые основные функции, известные в объектно-ориентированных языках, таких как наследование.
Основная декларация
Основная структура объявляется следующим образом:
type User struct {
FirstName, LastName string
Email string
Age int
}
Каждое значение называется полем. Поля обычно записываются по одному в строке, причем имя поля предшествует его типу. Последовательные поля того же типа могут быть объединены, как FirstName
и LastName
в приведенном выше примере.
Экспортированные или неэкспонированные поля (Private vs Public)
Поля структуры, названия которых начинаются с буквы верхнего регистра, экспортируются. Все остальные имена не показаны.
type Account struct {
UserID int // exported
accessToken string // unexported
}
Доступ к незагруженным полям возможен только с помощью кода внутри одного пакета. Таким образом, если вы когда-либо обращаетесь к полю из другого пакета, его имя должно начинаться с буквы верхнего регистра.
package main
import "bank"
func main() {
var x = &bank.Account{
UserID: 1, // this works fine
accessToken: "one", // this does not work, since accessToken is unexported
}
}
Однако изнутри bank
пакета вы можете без проблем получать доступ как к UserId, так и к accessToken.
bank
пакетов можно реализовать следующим образом:
package bank
type Account struct {
UserID int
accessToken string
}
func ProcessUser(u *Account) {
u.accessToken = doSomething(u) // ProcessUser() can access u.accessToken because
// it's defined in the same package
}
Композиция и вложение
Композиция обеспечивает альтернативу наследованию. Структура может включать другой тип по имени в своем объявлении:
type Request struct {
Resource string
}
type AuthenticatedRequest struct {
Request
Username, Password string
}
В приведенном выше примере AuthenticatedRequest
будет содержать четыре открытых элемента: Resource
, Request
, Username
и Password
.
Композитные структуры могут быть созданы и использованы так же, как и обычные структуры:
func main() {
ar := new(AuthenticatedRequest)
ar.Resource = "example.com/request"
ar.Username = "bob"
ar.Password = "P@ssw0rd"
fmt.Printf("%#v", ar)
}
Встраивание
В предыдущем примере Request
является встроенным полем. Состав также может быть достигнут путем встраивания другого типа. Это полезно, например, для того, чтобы украсить Struct с большей функциональностью. Например, продолжая работу с примером Resource, нам нужна функция, которая форматирует содержимое поля Resource, чтобы префикс его с помощью http://
или https://
. У нас есть два варианта: создать новые методы в AuthenticatedRequest или вставить их из другой структуры:
type ResourceFormatter struct {}
func(r *ResourceFormatter) FormatHTTP(resource string) string {
return fmt.Sprintf("http://%s", resource)
}
func(r *ResourceFormatter) FormatHTTPS(resource string) string {
return fmt.Sprintf("https://%s", resource)
}
type AuthenticatedRequest struct {
Request
Username, Password string
ResourceFormatter
}
И теперь основная функция может сделать следующее:
func main() {
ar := new(AuthenticatedRequest)
ar.Resource = "www.example.com/request"
ar.Username = "bob"
ar.Password = "P@ssw0rd"
println(ar.FormatHTTP(ar.Resource))
println(ar.FormatHTTPS(ar.Resource))
fmt.Printf("%#v", ar)
}
Посмотрите, что AuthenticatedRequest
имеет встроенную структуру ResourceFormatter
.
Но недостатком этого является то, что вы не можете получить доступ к объектам вне вашей композиции. Поэтому ResourceFormatter
не может получить доступ к элементам из AuthenticatedRequest
.
методы
Методы Struct очень похожи на функции:
type User struct {
name string
}
func (u User) Name() string {
return u.name
}
func (u *User) SetName(newName string) {
u.name = newName
}
Единственное различие заключается в добавлении приемника метода. Он может быть объявлен либо экземпляром типа, либо указателем на экземпляр типа. Поскольку SetName()
мутирует экземпляр, приемник должен быть указателем, чтобы произвести постоянное изменение в экземпляре.
Например:
package main
import "fmt"
type User struct {
name string
}
func (u User) Name() string {
return u.name
}
func (u *User) SetName(newName string) {
u.name = newName
}
func main() {
var me User
me.SetName("Slim Shady")
fmt.Println("My name is", me.Name())
}
Анонимная структура
Можно создать анонимную структуру:
data := struct {
Number int
Text string
} {
42,
"Hello world!",
}
Полный пример:
package main
import (
"fmt"
)
func main() {
data := struct {Number int; Text string}{42, "Hello world!"} // anonymous struct
fmt.Printf("%+v\n", data)
}
Теги
Поля Struct могут иметь теги, связанные с ними. Эти теги могут считываться пакетом reflect
, чтобы разработчик мог получить пользовательскую информацию, указанную в поле.
struct Account {
Username string `json:"username"`
DisplayName string `json:"display_name"`
FavoriteColor string `json:"favorite_color,omitempty"`
}
В приведенном выше примере теги используются для изменения имен ключей, используемых пакетом encoding/json
при маршалинге или размонтировании JSON.
Хотя тег может быть любым строковым значением, считается, что наилучшей практикой является использование разделенного пробелом key:"value"
пары key:"value"
:
struct StructName {
FieldName int `package1:"customdata,moredata" package2:"info"`
}
Теги структуры, используемые с encoding/xml
и encoding/json
, используются во всей стандартной библиотеке.
Создание структурных копий.
Структуру можно просто скопировать с помощью назначения.
type T struct {
I int
S string
}
// initialize a struct
t := T{1, "one"}
// make struct copy
u := t // u has its field values equal to t
if u == t { // true
fmt.Println("u and t are equal") // Prints: "u and t are equal"
}
В приведенном выше случае 't'
и «u» теперь являются отдельными объектами (значениями структуры).
Так как T
не содержит каких-либо ссылочных типов (срезов, карт, каналов) в качестве своих полей, t
и u
выше могут быть изменены, не затрагивая друг друга.
fmt.Printf("t.I = %d, u.I = %d\n", t.I, u.I) // t.I = 100, u.I = 1
Однако, если T
содержит ссылочный тип, например:
type T struct {
I int
S string
xs []int // a slice is a reference type
}
Тогда простая копия по присваиванию будет копировать значение поля типа среза также и для нового объекта. Это приведет к двум различным объектам, относящимся к одному и тому же объекту среза.
// initialize a struct
t := T{I: 1, S: "one", xs: []int{1, 2, 3}}
// make struct copy
u := t // u has its field values equal to t
Так как u и t относятся к одному и тому же срезу через их поле xs, обновление значения в срезе одного объекта будет отражать изменение в другом.
// update a slice field in u
u.xs[1] = 500
fmt.Printf("t.xs = %d, u.xs = %d\n", t.xs, u.xs)
// t.xs = [1 500 3], u.xs = [1 500 3]
Следовательно, необходимо проявлять особую осторожность, чтобы гарантировать, что это свойство ссылочного типа не создает непреднамеренного поведения.
Например, для копирования над объектами может быть выполнена явная копия поля среза:
// explicitly initialize u's slice field
u.xs = make([]int, len(t.xs))
// copy the slice values over from t
copy(u.xs, t.xs)
// updating slice value in u will not affect t
u.xs[1] = 500
fmt.Printf("t.xs = %d, u.xs = %d\n", t.xs, u.xs)
// t.xs = [1 2 3], u.xs = [1 500 3]
Структурные литералы
Значение типа struct может быть записано с использованием строкового литерала, который задает значения для его полей.
type Point struct { X, Y int }
p := Point{1, 2}
В приведенном выше примере указано каждое поле в правильном порядке. Это не полезно, потому что программисты должны помнить точные поля в порядке. Чаще всего структура может быть инициализирована путем перечисления некоторых или всех названий полей и их соответствующих значений.
anim := gif.GIF{LoopCount: nframes}
Поля опущены к нулевому значению для его типа.
Примечание . Две формы нельзя смешивать в одном и том же литерале.
Пустая структура
Структура представляет собой последовательность именованных элементов, называемых полями, каждая из которых имеет имя и тип. Пустая структура не имеет полей, вроде этой анонимной пустой структуры:
var s struct{}
Или как это называется пустым типом структуры:
type T struct{}
Интересная вещь о пустой структуре состоит в том, что ее размер равен нулю (попробуйте The Go Playground ):
fmt.Println(unsafe.Sizeof(s))
Это печатает 0
, поэтому пустая структура сама по себе не занимает памяти. поэтому это хороший вариант для выхода из канала, например (попробуйте The Go Playground ):
package main
import (
"fmt"
"time"
)
func main() {
done := make(chan struct{})
go func() {
time.Sleep(1 * time.Second)
close(done)
}()
fmt.Println("Wait...")
<-done
fmt.Println("done.")
}