← Volver al listado de tecnologías

Structs

Por: Artiko
gostructsembeddingcomposicion

Los structs son el mecanismo principal de Go para agrupar datos relacionados. A diferencia de lenguajes con clases, Go usa composicion sobre herencia mediante structs y embedding.

Declaracion de Struct

Un struct agrupa campos con nombre y tipo:

type Persona struct {
    Nombre string
    Edad   int
    Email  string
}

func main() {
    p := Persona{
        Nombre: "Ana",
        Edad:   30,
        Email:  "[email protected]",
    }
    fmt.Println(p.Nombre) // Ana
}

Campos Exportados vs No Exportados

La visibilidad se controla con la primera letra del campo:

ConvensionVisibilidadEjemplo
MayusculaExportado (publico)Nombre string
MinusculaNo exportado (privado al paquete)edad int
type Usuario struct {
    Nombre   string // Visible fuera del paquete
    password string // Solo accesible dentro del paquete
}

Esto aplica tanto a campos como al nombre del struct mismo.

Struct Literals

Hay tres formas de crear un struct:

func main() {
    // 1. Con nombres de campo (recomendado)
    p1 := Persona{Nombre: "Luis", Edad: 25}

    // 2. Sin nombres (fragil, depende del orden)
    p2 := Persona{"Luis", 25, "[email protected]"}

    // 3. Valor cero (campos en su zero value)
    var p3 Persona
    fmt.Println(p3.Nombre) // "" (string vacio)
    fmt.Println(p3.Edad)   // 0
}

Siempre usa la forma con nombres de campo. Es mas legible y resistente a cambios.

Structs Anonimos

Utiles para datos temporales sin necesidad de definir un tipo:

func main() {
    config := struct {
        Host string
        Port int
    }{
        Host: "localhost",
        Port: 8080,
    }
    fmt.Printf("%s:%d\n", config.Host, config.Port)
}

Son comunes en tests y para deserializar JSON puntual.

Punteros a Structs

Go permite acceder a campos de un puntero sin desreferencia explicita:

func main() {
    p := &Persona{Nombre: "Carlos", Edad: 40}

    // Go desreferencia automaticamente
    fmt.Println(p.Nombre) // equivale a (*p).Nombre

    // Modificar a traves del puntero
    p.Edad = 41
    fmt.Println(p.Edad) // 41
}

Usa punteros cuando necesites modificar el struct o evitar copias costosas.

Embedding (Composicion)

Go favorece composicion sobre herencia. Con embedding, un struct incluye los campos y metodos de otro:

type Direccion struct {
    Calle  string
    Ciudad string
    Pais   string
}

type Empleado struct {
    Nombre    string
    Direccion // campo embebido (sin nombre)
}

func main() {
    e := Empleado{
        Nombre: "Maria",
        Direccion: Direccion{
            Calle:  "Av. Principal 123",
            Ciudad: "CDMX",
            Pais:   "Mexico",
        },
    }

    // Acceso directo a campos promovidos
    fmt.Println(e.Ciudad) // CDMX
    // Tambien accesible de forma explicita
    fmt.Println(e.Direccion.Ciudad) // CDMX
}

Si hay conflicto de nombres, debes usar la ruta completa:

type Motor struct {
    Potencia int
}

type Chasis struct {
    Potencia int // mismo nombre que Motor
}

type Auto struct {
    Motor
    Chasis
}

func main() {
    a := Auto{}
    // a.Potencia  // ERROR: ambiguo
    a.Motor.Potencia = 150  // OK
    a.Chasis.Potencia = 200 // OK
}

Struct Tags

Los tags son metadatos asociados a campos, usados por paquetes como encoding/json y ORMs:

type Producto struct {
    ID          int     `json:"id" db:"product_id"`
    Nombre      string  `json:"nombre" db:"name"`
    Precio      float64 `json:"precio" db:"price"`
    EnInventario bool   `json:"en_inventario,omitempty"`
    Interno     string  `json:"-"` // excluido del JSON
}

func main() {
    p := Producto{
        ID:     1,
        Nombre: "Laptop",
        Precio: 999.99,
    }

    data, _ := json.Marshal(p)
    fmt.Println(string(data))
    // {"id":1,"nombre":"Laptop","precio":999.99}
    // en_inventario omitido por omitempty (valor cero)
}
TagUso
json:"nombre"Nombre en JSON
json:"-"Excluir del JSON
json:",omitempty"Omitir si es valor cero
db:"columna"Mapeo a columna de BD
validate:"required"Validacion con librerias

Funciones Constructoras (Patron New)

Go no tiene constructores, pero por convencion se usan funciones New:

type Servidor struct {
    host    string
    puerto  int
    timeout time.Duration
}

func NewServidor(host string, puerto int) *Servidor {
    return &Servidor{
        host:    host,
        puerto:  puerto,
        timeout: 30 * time.Second, // valor por defecto
    }
}

func main() {
    srv := NewServidor("localhost", 8080)
    fmt.Printf("Servidor en %s:%d\n", srv.host, srv.puerto)
}

Este patron permite validar parametros e inicializar valores por defecto.

Comparacion de Structs

Dos structs son comparables si todos sus campos son comparables:

type Punto struct {
    X, Y int
}

func main() {
    a := Punto{1, 2}
    b := Punto{1, 2}
    c := Punto{3, 4}

    fmt.Println(a == b) // true
    fmt.Println(a == c) // false
}

Structs con slices, maps o funciones no son comparables con ==:

type Datos struct {
    Valores []int // slice: no comparable
}

// d1 == d2 --> ERROR de compilacion
// Usa reflect.DeepEqual(d1, d2) si lo necesitas

Resumen

ConceptoDescripcion
StructAgrupacion de campos con tipo
ExportacionMayuscula = publico, minuscula = privado
EmbeddingComposicion: incluir un struct dentro de otro
TagsMetadatos para JSON, BD, validacion
ConstructorFuncion NewTipo() que retorna *Tipo
ComparacionSolo si todos los campos son comparables

← Capitulo 8: Slices y Maps | Capitulo 10: Metodos →