Buscar..


Introducción

Las estructuras son conjuntos de varias variables empaquetadas juntas. La estructura en sí misma es solo un paquete que contiene variables y que las hace fácilmente accesibles.

A diferencia de en C, las estructuras de Go pueden tener métodos adjuntos. También les permite implementar interfaces. Eso hace que las estructuras de Go sean similares a los objetos, pero faltan (probablemente intencionalmente) algunas características importantes conocidas en lenguajes orientados a objetos como la herencia.

Declaración Básica

Una estructura básica se declara de la siguiente manera:

type User struct {
    FirstName, LastName string
    Email               string
    Age                 int
}

Cada valor se llama un campo. Los campos generalmente se escriben uno por línea, con el nombre del campo precediendo a su tipo. Los campos consecutivos del mismo tipo se pueden combinar, como FirstName y LastName en el ejemplo anterior.

Campos exportados frente a no exportados (privado frente a público)

Los campos de Struct cuyos nombres comienzan con una letra mayúscula se exportan. Todos los demás nombres no son exportados.

type Account struct {
    UserID      int    // exported
    accessToken string // unexported
}

Solo se puede acceder a los campos no exportados por código dentro del mismo paquete. Como tal, si alguna vez accede a un campo de un paquete diferente , su nombre debe comenzar con una letra mayúscula.

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
    }
}

Sin embargo, desde el paquete del bank , puede acceder tanto a UserId como a accessToken sin problemas.

El bank paquetes podría implementarse así:

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
}

Composición e incrustación

La composición proporciona una alternativa a la herencia. Una estructura puede incluir otro tipo por nombre en su declaración:

type Request struct {
    Resource string
}

type AuthenticatedRequest struct {
    Request
    Username, Password string
}

En el ejemplo anterior, AuthenticatedRequest contendrá cuatro miembros públicos: Resource , Request , Username y Password .

Las estructuras compuestas se pueden instanciar y utilizar de la misma manera que las estructuras normales:

func main() {
    ar := new(AuthenticatedRequest)
    ar.Resource = "example.com/request"
    ar.Username = "bob"
    ar.Password = "P@ssw0rd"
    fmt.Printf("%#v", ar)
}

jugar en el patio de recreo

Incrustación

En el ejemplo anterior, la Request es un campo incrustado. La composición también se puede lograr mediante la incrustación de un tipo diferente. Esto es útil, por ejemplo, para decorar un Struct con más funcionalidad. Por ejemplo, continuando con el ejemplo de Recursos, queremos una función que formatee el contenido del campo de Recursos para prefijarlo con http:// o https:// . Tenemos dos opciones: crear los nuevos métodos en AuthenticatedRequest o incrustarlos desde una estructura diferente:

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
}

Y ahora la función principal podría hacer lo siguiente:

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)
}

Observe que el AuthenticatedRequest que tiene una estructura incorporada ResourceFormatter .

Pero la desventaja es que no puedes acceder a objetos fuera de tu composición. Por lo tanto, ResourceFormatter no puede acceder a los miembros de AuthenticatedRequest .

jugar en el patio de recreo

Métodos

Los métodos de Struct son muy similares a las funciones:

type User struct {
    name string
}

func (u User) Name() string {
    return u.name
}

func (u *User) SetName(newName string) {
    u.name = newName
}

La única diferencia es la adición del método receptor. Se puede declarar como una instancia del tipo o un puntero a una instancia del tipo. Como SetName() muta la instancia, el receptor debe ser un puntero para realizar un cambio permanente en la instancia.

Por ejemplo:

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())
}

Ir al patio de recreo

Estructura anónima

Es posible crear una estructura anónima:

data := struct {
    Number int 
    Text   string
} { 
    42, 
    "Hello world!",
}

Ejemplo completo:

package main

import (
    "fmt"
)

func main() {
    data := struct {Number int; Text string}{42, "Hello world!"} // anonymous struct
    fmt.Printf("%+v\n", data)
}

jugar en el patio de recreo

Etiquetas

Los campos Struct pueden tener etiquetas asociadas con ellos. Estas etiquetas pueden ser leídas por el reflect paquete para obtener información personalizada especificada sobre un campo por el desarrollador.

struct Account {
    Username      string `json:"username"`
    DisplayName   string `json:"display_name"`
    FavoriteColor string `json:"favorite_color,omitempty"`
}

En el ejemplo anterior, las etiquetas se usan para cambiar los nombres de clave utilizados por el paquete encoding/json al calcular o desmarcar JSON.

Si bien la etiqueta puede tener cualquier valor de cadena, se considera una práctica recomendada usar los pares de key:"value" separados por espacios:

struct StructName {
    FieldName int `package1:"customdata,moredata" package2:"info"`
}

Las etiquetas de estructura utilizadas con el paquete encoding/xml y encoding/json se utilizan en todo el libarary estándar.

Haciendo copias struct.

Una estructura puede simplemente copiarse usando una asignación.

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"
}

En el caso anterior, 't' y 'u' ahora son objetos separados (valores de estructura).

Dado que T no contiene ningún tipo de referencia (segmentos, mapa, canales) ya que sus campos, t y u anteriores pueden modificarse sin afectarse entre sí.

fmt.Printf("t.I = %d, u.I = %d\n", t.I, u.I) // t.I = 100, u.I = 1

Sin embargo, si T contiene un tipo de referencia, por ejemplo:

type T struct {
    I  int
    S  string
    xs []int // a slice is a reference type
}

Luego, una copia por asignación simple copiaría también el valor del campo de tipo de sector en el nuevo objeto. Esto daría como resultado dos objetos diferentes que se refieren al mismo objeto de sector.

// 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

Dado que tanto u como t hacen referencia a la misma división a través de su campo xs, la actualización de un valor en la división de un objeto reflejaría el cambio en la otra.

// 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]

Por lo tanto, se debe tener mucho cuidado para garantizar que esta propiedad de tipo de referencia no produzca un comportamiento involuntario.

Para copiar los objetos anteriores, por ejemplo, se podría realizar una copia explícita del campo de división:

// 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]

Literales de Struct

Un valor de un tipo de estructura se puede escribir utilizando un literal de estructura que especifica valores para sus campos.

type Point struct { X, Y int }
p := Point{1, 2}

El ejemplo anterior especifica cada campo en el orden correcto. Lo que no es útil, porque los programadores tienen que recordar los campos exactos en orden. Más a menudo, una estructura se puede inicializar enumerando algunos o todos los nombres de campo y sus valores correspondientes.

anim := gif.GIF{LoopCount: nframes}

Los campos omitidos se establecen en el valor cero para su tipo.

Nota: Las dos formas no se pueden mezclar en el mismo literal.

Estructura vacía

Una estructura es una secuencia de elementos con nombre, campos llamados, cada uno de los cuales tiene un nombre y un tipo. La estructura vacía no tiene campos, como esta estructura vacía anónima:

var s struct{}

O como este tipo de estructura vacía nombrada:

type T struct{}

Lo interesante de la estructura vacía es que, su tamaño es cero (prueba The Go Playground ):

fmt.Println(unsafe.Sizeof(s))

Esto imprime 0 , por lo que la estructura vacía no toma memoria. así que es una buena opción para salir del canal, como (prueba 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
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow