Exercício – Usar structs

Concluído

Há ocasiões em que você precisará representar uma coleção de campos em uma estrutura. Por exemplo, quando você precisa escrever um programa de folha de pagamento, é necessário usar uma estrutura de dados de funcionários. No Go, você pode usar structs para agrupar campos diferentes que podem formar um registro.

Um struct no Go é outro tipo de dados que pode conter zero ou mais campos de tipos arbitrários e representá-los como uma só entidade.

Nesta seção, exploraremos por que os structs são essenciais e como usá-los.

Declarar e inicializar um struct

Para declarar um struct, você precisará usar a palavra-chave struct, junto com a lista de campos e os respectivos tipos que o novo tipo de dados deve ter. Por exemplo, para definir um struct de funcionários, use o seguinte código:

type Employee struct {
    ID        int
    FirstName string
    LastName  string
    Address   string
}

Depois, declare uma variável com o novo tipo como faria normalmente com outros tipos, desta forma:

var john Employee

E se quiser declarar e inicializar uma variável ao mesmo tempo, faça isso da seguinte maneira:

employee := Employee{1001, "John", "Doe", "Doe's Street"}

Observe que você precisará especificar um valor para cada um dos campos do struct. Mas isso pode ser problemático às vezes. Como alternativa, você pode ser mais específico sobre os campos que deseja inicializar em um struct:

employee := Employee{LastName: "Doe", FirstName: "John"}

Observe que, na instrução anterior, a ordem em que você atribui valores a cada campo não importa. Além disso, não importa se você não especifica um valor para nenhum outro campo. O Go atribuirá um valor padrão dependendo do tipo de dados do campo.

Para acessar campos individuais de um struct, faça isso usando a notação de ponto (.), como neste exemplo:

employee.ID = 1001
fmt.Println(employee.FirstName)

Por fim, use o operador & para produzir um ponteiro para o struct, como demonstra o seguinte código:

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

Ao executar o código anterior, você verá a seguinte saída:

{0 John Doe }
{0 David Doe }

Observe como o struct se torna mutável quando você usa ponteiros.

Inserção de structs

Os structs do Go permitem que você insira outro struct em um struct. Haverá ocasiões em que você desejará reduzir a repetição e reutilizar um struct comum. Por exemplo, digamos que você queira refatorar o código anterior para ter um tipo de dados para um funcionário e outro para um prestador de serviço. Você pode ter um struct Person que contenha campos comuns, como neste exemplo:

type Person struct {
    ID        int
    FirstName string
    LastName  string
    Address   string
}

Em seguida, você pode declarar outros tipos que inserem um tipo Person, como um Employee e um Contractor. Para inserir outro struct, você criará um campo, como neste exemplo:

type Employee struct {
    Information Person
    ManagerID   int
}

Mas para referenciar um campo por meio do struct Person, inclua o campo Information de uma variável de funcionário, como neste exemplo:

var employee Employee
employee.Information.FirstName = "John"

Se você estiver refatorando o código como estamos fazendo, isso interromperá nosso código. Como alternativa, você pode incluir um novo campo com o mesmo nome do struct que está inserindo, como neste exemplo:

type Employee struct {
    Person
    ManagerID int
}

Como demonstração, você pode usar o seguinte código:

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 como você acessa o campo FirstName por meio de um struct Employee sem precisar especificar o campo Person, pois ele está inserindo todos os campos automaticamente. Mas, ao inicializar um struct, você precisará ser específico sobre a qual campo deseja atribuir um valor.

Codificar e decodificar structs com JSON

Por fim, você pode usar structs para codificar e decodificar dados em JSON. O Go tem suporte excelente para o formato JSON e ele já está incluído nos pacotes da biblioteca padrão.

Você também pode realizar ações como renomear o nome de um campo por meio do struct. Por exemplo, digamos que você não queira que a saída JSON mostre FirstName, mas simplesmente name ou ignore os campos vazios. Use marcas de campo como mostra este exemplo:

type Person struct {
    ID        int    
    FirstName string `json:"name"`
    LastName  string
    Address   string `json:"address,omitempty"`
}

Em seguida, para codificar um struct em JSON, use a função json.Marshal. Para decodificar uma cadeia de caracteres JSON em uma estrutura de dados, use a função json.Unmarshal. Veja um exemplo que reúne tudo: a codificação de uma matriz de funcionários em JSON e a decodificação da saída em uma nova variável:

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

Ao executar o código anterior, você verá a seguinte saída:

[{"ID":0,"name":"John","LastName":"Doe","ManagerID":0},{"ID":0,"name":"David","LastName":"Campbell","ManagerID":0}]
[{{0 John Doe } 0} {{0 David Campbell } 0}]