Introducción

La capacidad para crear abstracciones en torno a detalles concretos es la mejor herramienta que un lenguaje de programación puede ofrecer a un desarrollador. Las “structs” permiten que los desarrolladores de Go describan el mundo en el que un programa de Go funciona. En vez de razonar sobre cadenas que describen una Street, una City o un PostalCode, las structs nos permiten hablar sobre una Address. Sirven como un nexo natural para la documentación en nuestros esfuerzos por indicar a desarrolladores futuros (incluidos nosotros mismos) los datos que son importantes para nuestros programas de Go y la forma en que el código futuro debería usar esos datos como corresponde. Las structs pueden definirse de varias formas diferentes. En este tutorial, veremos cada una de estas técnicas.

Definir structs

Las structs funcionan como formularios en papel que podría usar, por ejemplo, para declarar sus impuestos. Los formularios en papel tienen campos para datos textuales, como su nombre y apellido. A parte de los campos de texto, los formularios pueden tener casillas de verificación para indicar valores booleanos como “married” (casado) o “single” (soltero), o campos de fecha para la fecha de nacimiento. De forma similar, las structs recogen diferentes datos y los organizan con diferentes nombres de campos. Cuando inicia una variable con una nueva struct, es como si fotocopiase un formulario y lo dejase listo para completarse.

Para crear una nueva struct, primero debe proporcionar a Go un esquema que describa los campos que esta contiene. Esta definición de struct normalmente comienza con la palabra clave type seguida por el nombre de la struct. Después de esto, utilice la palabra clave struct seguida de un par de llaves {} en el que declare los campos que tendrá la struct. Una vez que defina la struct, podrá declarar las variables que usan esta definición de struct. En este ejemplo se define y se utiliza una struct:

package main

import "fmt"

type Creature struct {
    Name string
}

func main() {
    c := Creature{
        Name: "Sammy the Shark",
    }
    fmt.Println(c.Name)
}

Cuando ejecute este código, verá este resultado:

output
Sammy the Shark

Primero definimos una struct Creature en este ejemplo, la cual contiene un campo Name de tipo string. En el cuerpo de main, creamos una instancia de Creature colocando un par de corchetes después del nombre del tipo, Creature, y luego especificando los valores para los campos de esa instancia. El campo Name de la instancia c contendrá “Sammy the Shark”. En la invocación de la función fmt.PrintIn, obtenemos los valores del campo de instancia ubicando un punto después de la variable en la que se creó la instancia y, después de esto, el nombre del campo al que queremos acceder. Por ejemplo, c.Name en este caso muestra el campo Name.

Cuando se declara una nueva instancia de una struct, generalmente se enumeran los nombres de campos con sus valores, como en el último ejemplo. También se pueden omitir los nombres de campos, si el valor de cada campo se proporciona durante la creación de instancias de una struct, como en este ejemplo:

package main

import "fmt"

type Creature struct {
    Name string
    Type string
}

func main() {
    c := Creature{"Sammy", "Shark"}
    fmt.Println(c.Name, "the", c.Type)
}

El resultado es el mismo que el del último ejemplo:

output
Sammy the Shark

Agregamos un campo a Creature para realizar un seguimiento del Type de criatura como una string. Al crear una instancia de Creature en el cuerpo de main, optamos por usar la forma de creación de instancias más corta proporcionando valores para cada campo a fin de ordenar y omitir los nombres de estos. En la declaración Creature{"Sammy", "Shark"}, el campo Name toma el valor Sammy y el campo Type toma el valor Shark porque Name aparece primero en la declaración de tipo, seguido de Type.

Esta forma de declaración más corta tiene algunas desventajas que hicieron que la comunidad Go prefiriera la forma más larga en la mayoría de las circunstancias. Debe proporcionar valores para cada campo en la struct al utilizar la declaración corta; no puede omitir los campos que no le interesan. Esto rápidamente hace que las declaraciones cortas para las structs con muchos campos se vuelvan confusas. Por este motivo, declarar structs usando la forma corta es común con structs que tienen pocos campos.

Los nombres de campo de los ejemplos hasta el momento comenzaron con letras mayúsculas. Esto es más importante que una preferencia estilística. El uso de letras mayúsculas o minúsculas para nombres de campos determina si el código que se ejecute en otros paquetes podrá acceder a ellos.

Exportación de campos de struct

Los campos de una struct siguen las mismas reglas de exportación que otros identificadores del lenguaje de programación Go. Si un nombre de campo comienza con una letra mayúscula, será legible y se podrá escribir a través de código que se encuentre fuera del paquete en el que se definió la struct. Si el campo comienza con una letra minúscula, solo el código dentro del paquete de esa struct podrá realizar tareas de lectura y escritura en ese campo. En este ejemplo definen los campos que se exportan y los que no:

package main

import "fmt"

type Creature struct {
    Name string
    Type string

    password string
}

func main() {
    c := Creature{
        Name: "Sammy",
        Type: "Shark",

        password: "secret",
    }
    fmt.Println(c.Name, "the", c.Type)
    fmt.Println("Password is", c.password)
}

El resultado será el siguiente:

output
Sammy the Shark Password is secret

Agregamos un campo a nuestros ejemplos anteriores: secret. secret es una string no exportada, lo cual significa que cualquier otro paquete que intente crear una instancia de una Creature no podrá acceder a su campo secret ni configurarlo. En el mismo paquete, podemos acceder a estos campos, como en este ejemplo. Ya que main está también en el paquete main, puede hacer referencia a c.password y obtener el valor almacenado allí. Es común que haya campos no exportado en structs con acceso a ellos mediado por los métodos exportados.

Structs en línea

Además de definir un nuevo tipo para representar una struct, puede definir una struct en línea. Estas definiciones de structs sobre la marcha son útiles en situaciones en las cuales inventar nuevos nombres para los tipos de structs sería un esfuerzo inútil. Por ejemplo, en las pruebas a menudo se utilizan un struct para definir todos los parámetros que forman un caso de prueba concreto. Sería engorroso pensar en nuevos nombres como CreatureNamePrintingTestCase cuando esa struct se utiliza en un único lugar.

Las definiciones de structs en línea aparecen en el lado derecho de una asignación de variable. Debe proporcionar una creación de instancias de ellas de inmediato proveyendo un par adicional de corchetes con valores para cada uno de los campos que defina. En el siguiente ejemplo se muestra la definición de una struct en línea:

package main

import "fmt"

func main() {
    c := struct {
        Name string
        Type string
    }{
        Name: "Sammy",
        Type: "Shark",
    }
    fmt.Println(c.Name, "the", c.Type)
}

El resultado de este ejemplo será el siguiente:

output
Sammy the Shark

En vez de definir un nuevo tipo que describa nuestra struct con la palabra clave type, este ejemplo define una struct en línea disponiendo la definición de struct inmediatamente después el operador de asignación corta :=. Definimos los campos de la struct como en los ejemplos anteriores, pero luego debemos proporcionar de inmediato otro par de llaves y los valores que cada campo asumirá. Esta struct ahora se usa exactamente de la misma manera que antes; podemos hacer referencia a los nombres de campos usando la notación de puntos. Verá structs en línea declaradas con mayor frecuencia durante pruebas, ya que a menudo las structs puntuales se definen para contener datos y expectativas para un caso de prueba concreto.

Conclusión

Las structs son colecciones de datos heterogéneos definidas por los programadores para organizar la información. La mayoría de los programas manejan enormes volúmenes de datos, y sin structs sería difícil recordar las variables string o int que debían estar juntas o las que eran diferentes. La próxima vez que se encuentre haciendo malabares con grupos de variables, pregúntese si quizá esas variables estarían mejor agrupadas con una struct. Es posible que esas variables hayan descrito un concepto de nivel más alto todo el tiempo.

0 Comments

Creative Commons License