Поиск…


Вступление

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

В отличие от 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.")
}



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow