Introdução

Compilar abstrações em volta de detalhes concretos é a maior ferramenta que uma linguagem de programação pode dar a um desenvolvedor. O struct permite que os desenvolvedores do Go descrevam o mundo no qual um programa em Go opera. Em vez de pensar em strings descrevendo uma Street (Rua), City (Cidade) ou um PostalCode (CEP), os structs nos permitem falar sobre um Address (Endereço). Eles servem como uma ligação natural com a documentação em nossos esforços para dizer aos futuros desenvolvedores (incluindo nós mesmos) quais dados são importantes para nossos programas em Go e como o código futuro deve usar esses dados de maneira correta. Os structs podem ser definido e usados de diferentes maneiras. Neste tutorial, vamos dar uma olhada em cada uma dessas técnicas.

Definindo Structs

Os Structs funcionam como formulários de papel que você pode usar, por exemplo, para registrar seus impostos. Os formulários de papel podem ter campos para informações de texto como seu nome e sobrenome. Além dos campos de texto, os formulários podem ter caixas de seleção para indicar valores booleanos, como “casado” ou “solteiro”, ou campos de datas para a data de nascimento. De igual modo, os structs coletam diferentes dados e os organizam sob diferentes nomes de campo. Quando você inicializa uma variável com um novo struct, é como se você tivesse feito uma cópia de um formulário e o deixou pronto para preencher.

Para criar um novo struct, você deve primeiro dar ao Go um plano gráfico que descreva os campos do struct. Essa definição do struct geralmente começa com a palavra-chave type, seguida pelo nome do struct. Após isso, utilize a palavra-chave struct, seguida de um par de chaves {}, onde você declara os campos que o struct irá conter. Assim que definir o struct, você poderá, então, declarar as variáveis que usam essa definição de struct. Este exemplo define um struct e o usa:

package main

import "fmt"

type Creature struct {
    Name string
}

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

Quando você executar esse código, verá o seguinte resultado:

output
Sammy the Shark

Neste exemplo, primeiro definimos um struct Creature, contendo um campo Name do tipo string. Dentro do corpo do main, criamos uma instância de Creature, colocando um par de chaves após o nome do tipo, Creature e, em seguida, especificamos os valores para os campos daquela instância. A instância em c terá seu campo Name configurado para “Sammy the Shark”(Sammy, o tubarão). Dentro da invocação da função fmt.Println, recuperamos os valores do campo da instância, colocando um ponto após a variável em que a instância foi criada, seguida pelo nome do campo que gostaríamos de acessar. Por exemplo, o c.Name, neste caso, retorna o campo Name.

Ao declarar uma nova instância de um struct, você geralmente enumera os nomes de campo com seus valores, como no último exemplo. De forma alternativa, se cada valor de campo for fornecido durante a instanciação de um struct, você pode omitir os nomes de campo, como neste exemplo:

package main

import "fmt"

type Creature struct {
    Name string
    Type string
}

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

O resultado é o mesmo que o do último exemplo:

output
Sammy the Shark

Adicionamos um campo extra para a Creature rastrear o Type de criatura como uma string. Ao instanciar a Creature dentro do corpo do main, optamos pelo uso do formulário de instanciação mais curto, fornecendo valores para cada campo em ordem e omitindo seus nomes de campo. Na declaração Creature{"Sammy", "Shark"}, o campo Name recebe o valor Sammy e o campo Type recebe o valor Shark, pois o Name aparece primeiro na declaração de tipo, seguido pelo Type.

Este formulário de declaração mais curto tem algumas desvantagens que levaram a comunidade Go a preferir o formulário mais longo, na maioria das circunstâncias. Você deve fornecer os valores de cada campo no struct ao usar a declaração curta — você não pode ignorar os campos com os quais não se importa. Isso faz com que as declarações curtas para structs com muitos campos se tornem confusas rapidamente. Por esse motivo, declarar os structs usando o formulário curto é normalmente usado com structs que têm poucos campos.

Até agora, os nomes de campos nos exemplos começaram todos com letras maiúsculas. Isso é mais significativo do que uma preferência estilística. O uso de letras maiúsculas ou minúsculas para os nomes dos campos afeta a acessibilidade dos seus nomes de campos pelos códigos em execução em outros pacotes.

Exportação do campo de struct

Os campos de um struct seguem as mesmas regras de exportação que outros identificadores dentro da linguagem de programação Go. Se um nome de campo iniciar com uma letra maiúscula, ele será legível e gravável pelo código fora do pacote onde o struct foi definido. Se o campo iniciar com uma letra minúscula, apenas o código dentro do pacote do struct poderá ler e gravar naquele campo. Este exemplo define os campos que são exportados e os que não são:

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

Isso resultará em:

output
Sammy the Shark Password is secret

Acrescentamos um campo adicional aos nossos exemplos anteriores, secret.secret, que é um campo de string não exportado, o que significa que qualquer outro pacote que tentar instanciar uma Creature não poderá acessar ou definir seu campo secret. Dentro do mesmo pacote, podemos acessar esses campos, como esse exemplo fez. Como o main também está no pacote main, ele pode referir o c.password e recuperar o valor armazenado lá. É comum ter campos não exportados em structs com acesso a eles e mediados por métodos exportados.

Structs em linha

Além de definir um novo tipo para representar um struct, você também pode definir um struct em linha. Essas definições de struct dinâmicas são úteis em situações onde a criação de novos nomes para tipos de struct seriam um esforço perdido. Por exemplo, com frequência os testes usam um struct para definir todos os parâmetros que compõem um caso de teste em particular. Seria complicado inventar novos nomes como CreatureNamePrintingTestCase quando esse struct é usado em apenas um lugar.

As definições de struct em linha aparecem à direita de uma atribuição de variável. Você deve fornecer uma instanciação deles imediatamente após, fornecendo um par de chaves adicional com os valores de cada um dos campos que você definir. O exemplo seguinte mostra uma definição de um struct em linha:

package main

import "fmt"

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

O resultado deste exemplo será:

output
Sammy the Shark

Em vez de definir um novo tipo descrevendo nosso struct com a palavra-chave type, esse exemplo define um struct em linha colocando a definição do struct imediatamente após o operador de atribuição curto, :=. Definimos os campos do struct como nos exemplos anteriores, mas devemos fornecer imediatamente outro par de chaves e os valores que cada campo irá assumir. Agora, o uso desse struct ficou exatamente como era antes — podemo-nos referir aos nomes de campo usando a notação de ponto. O lugar mais comum em que você verá os structs declarados em linha será durante testes, já que os structs únicos são definidos para conter dados e expectativas de um caso de teste em particular.

Conclusão

Os structs são coleções de dados heterogêneos, definidos pelo programadores para organizar informações. A maioria dos programas lida com volumes de dados imensos e, sem structs, seria difícil lembrar quais variáveis de string ou int pertenciam juntas ou quais eram diferentes. Da próxima vez que você se encontrar manipulando grupos de variáveis, pergunte a si mesmo se essas variáveis ficariam melhor agrupadas usando um struct. Essas variáveis podem estar descrevendo um conceito de nível superior desde o começo.

0 Comments

Creative Commons License