Ejercicio: Uso de structs
Hay ocasiones en las que es necesario representar una colección de campos en una estructura. Por ejemplo, si necesita escribir un programa de nóminas, debe usar una estructura de datos de empleados. En Go, puede usar structs para agrupar distintos campos que podrían formar un registro.
Un struct de Go es otro tipo de datos que puede contener cero o más campos de tipos arbitrarios y representarlos como una sola entidad.
En esta sección vamos a ver por qué los structs son esenciales y cómo usarlos.
Declaración e inicialización de un struct
Para declarar un struct, debe usar la palabra clave struct
junto con la lista de campos y los tipos que quiere que tenga el nuevo tipo de datos. Por ejemplo, para definir un struct de empleados, podría usar el código siguiente:
type Employee struct {
ID int
FirstName string
LastName string
Address string
}
Luego puede declarar una variable con el nuevo tipo como haría normalmente con otros tipos, así:
var john Employee
Si quiere declarar e inicializar una variable al mismo tiempo, puede hacerlo así:
employee := Employee{1001, "John", "Doe", "Doe's Street"}
Observe que debe especificar un valor para cada uno de los campos del struct. Pero eso podría ser problemático a veces. Como alternativa, puede ser más específico sobre los campos que quiere inicializar en un struct:
employee := Employee{LastName: "Doe", FirstName: "John"}
Observe que, desde la instrucción anterior, el orden en que se asignan los valores a cada campo no importa. Además, no importa si no se especifica ningún valor para ningún otro campo. Go asigna un valor predeterminado en función del tipo de datos del campo.
Para acceder a campos individuales de un struct, puede usar la notación de punto (.
), como en este ejemplo:
employee.ID = 1001
fmt.Println(employee.FirstName)
Por último, puede usar el operador &
para generar un puntero al struct, como se muestra en el código siguiente:
package main
import "fmt"
type Employee struct {
ID int
FirstName string
LastName string
Address string
}
func main() {
employee := Employee{LastName: "Doe", FirstName: "John"}
fmt.Println(employee)
employeeCopy := &employee
employeeCopy.FirstName = "David"
fmt.Println(employee)
}
Al ejecutar el código anterior se ve el siguiente resultado:
{0 John Doe }
{0 David Doe }
Observe cómo el struct se vuelve mutable cuando se usan punteros.
Inserción de structs
Los structs de Go permiten insertar un struct dentro de otro. Hay ocasiones en las que se quiere reducir la repetición y reutilizar un struct común. Por ejemplo, imagine que quiere refactorizar el código anterior para tener un tipo de datos para un empleado y otro para un contratista. Puede tener un struct Person
con campos comunes, como en este ejemplo:
type Person struct {
ID int
FirstName string
LastName string
Address string
}
Luego puede declarar otros tipos que insertan un tipo Person
, como Employee
y Contractor
. Para insertar otro struct, cree un nuevo campo, como en este ejemplo:
type Employee struct {
Information Person
ManagerID int
}
Pero para hacer referencia a un campo del struct Person
, debe incluir el campo Information
de una variable de empleado, como en este ejemplo:
var employee Employee
employee.Information.FirstName = "John"
Si va a refactorizar código como estamos haciendo ahora, eso interrumpiría el código. Como alternativa, basta con incluir un nuevo campo con el mismo nombre que el struct que está insertando, como en este ejemplo:
type Employee struct {
Person
ManagerID int
}
Como demostración, puede usar el código siguiente:
package main
import "fmt"
type Person struct {
ID int
FirstName string
LastName string
Address string
}
type Employee struct {
Person
ManagerID int
}
type Contractor struct {
Person
CompanyID int
}
func main() {
employee := Employee{
Person: Person{
FirstName: "John",
},
}
employee.LastName = "Doe"
fmt.Println(employee.FirstName)
}
Observe cómo se accede al campo FirstName
desde un struct Employee
sin tener que especificar el campo Person
, ya que está insertando todos sus campos automáticamente. Pero, al inicializar un struct, debe ser específico con respecto al campo al que quiere asignar un valor.
Codificación y descodificación de structs con JSON
Por último, puede usar structs para codificar y descodificar datos en JSON. Go tiene una excelente compatibilidad con el formato JSON y ya está incluido en los paquetes de biblioteca estándar.
También puede hacer cosas como cambiar el nombre de un campo del struct. Por ejemplo, imagine que no quiere que el resultado JSON muestre FirstName
, sino simplemente name
, o que omita los campos vacíos. Puede usar etiquetas de campo como se muestra en este ejemplo:
type Person struct {
ID int
FirstName string `json:"name"`
LastName string
Address string `json:"address,omitempty"`
}
Luego, para codificar un struct en JSON, use la función json.Marshal
. Para descodificar una cadena JSON en una estructura de datos, use la función json.Unmarshal
. En este ejemplo se une todo: se codifica una matriz de empleados en JSON y se descodifica el resultado en una nueva variable:
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
ID int
FirstName string `json:"name"`
LastName string
Address string `json:"address,omitempty"`
}
type Employee struct {
Person
ManagerID int
}
type Contractor struct {
Person
CompanyID int
}
func main() {
employees := []Employee{
Employee{
Person: Person{
LastName: "Doe", FirstName: "John",
},
},
Employee{
Person: Person{
LastName: "Campbell", FirstName: "David",
},
},
}
data, _ := json.Marshal(employees)
fmt.Printf("%s\n", data)
var decoded []Employee
json.Unmarshal(data, &decoded)
fmt.Printf("%v", decoded)
}
Al ejecutar el código anterior se ve el siguiente resultado:
[{"ID":0,"name":"John","LastName":"Doe","ManagerID":0},{"ID":0,"name":"David","LastName":"Campbell","ManagerID":0}]
[{{0 John Doe } 0} {{0 David Campbell } 0}]