Conteúdo

Tipos compostos

Tipos compostos em Go são nada mais do que a combinação de múltiplos tipos primitivos para representar diferentes tipos de dados. Com os tipos compostos temos uma liberdade muito grande de modelar as informações da forma que melhor resolvem o problema com o qual estamos lidando. Os principais tipos compostos que usamos no nosso dia a dia são: arrays, slices, maps e structs.

Arrays

Um array guarda um número específico de elementos do mesmo tipo, que podem ser de tipos básicos, como int e string, ou do tipo struct. Em geral, eles são menos utilizados que slices, mas é importante entendê-los pois são a estrutura de dados básica dos slices e maps.

Representação de um array

índice

[0]

[1]

[2]

[3]

valor

127

290

7

83

tipo

int64

int64

int64

int64

Todos os elementos têm o mesmo tipo (int64) e cada um tem um índice que pode ser usado para acessá-lo (primeira linha). Se o array representado se chamasse numeros, utilizaríamos a sintaxe numeros[2] para acessar o elemento de índice 2 e valor 7.

Exemplo de declaração:

var diasDaSemana [7]string
diasDaSemana = [7]string{"Domingo", "Segunda", "Terça", "Quarta", "Quinta", "Sexta", "Sábado"}

Também é possível fazer a declaração usando o array literal, que permite declarar o tipo do array e atribuir valor a elementos na mesma operação.

Exemplo de declaração:

diasDaSemana := [7]string{"Domingo", "Segunda", "Terça", "Quarta", "Quinta", "Sexta", "Sábado"}

O tipo do array sempre leva em consideração sua quantidade de elementos. Por exemplo, o tipo de um array [5]string é diferente do tipo de um array [7]string. Caso se tente atribuir o valor de um ao outro, haverá um erro de compilação.

Exemplo de código com erro de compilação:

package main

func main() {
	diasDaSemana := [7]string{"Domingo", "Segunda", "Terça", "Quarta", "Quinta", "Sexta", "Sábado"}
	diasUteis := [5]string{"Segunda", "Terça", "Quarta", "Quinta", "Sexta"}
	diasDaSemana = diasUteis
}

Ao rodar o código acima, o erro ./prog.go:6:15: cannot use diasUteis (type [5]string) as type [7]string in assignment acontece em tempo de compilação.

O array de n elementos pode ser acessado pelos índices 0 ao n-1. No caso do array diasDaSemana, seu tipo é [7]string, seu tamanho é de 7 elementos, e seus índices vão de 0 a 6.

Slices

Slices são parecidos com arrays, mas oferecem uma flexibilidade muito maior para quem desenvolve principalmente por terem tamanho variável. Além disso, slices têm a função embutida append(), que permite adicionar mais elementos com facilidade - a conheceremos em breve! Em materiais traduzidos para o português, é comum slice ser traduzido como “fatia”.

A sintaxe de um slice é []T, sendo T o tipo que cada um dos elementos do slice terá. Note que, diferentemente de array, não há número entre os colchetes. Por exemplo, um slice de strings tem o tipo []string.

Exemplo de declaração:

Slice literal

Um slice sempre parte de um array, então vamos aproveitar a declaração de array do exemplo de cima e demonstrar a declaração e atribuição de valor usando um slice literal.

E se você não souber quais elementos vai precisar colocar no slice no momento de sua declaração?

Se você declarar seu slice da forma var nome []string e tentar inserir elementos através de seus índices, como no exemplo abaixo, receberá o erro index out of range em tempo de execução, pois esse slice foi criado com tamanho 0 e estamos tentando acessar índices inexistentes.

Usando append()

A função embutida append() pode ser utilizada para evitar esse cenário. Ela facilita o processo de estender o slice ao abstrair o aumento de seu tamanho e capacidade.

Exemplo:

Duas funções embutidas que também podem ser usadas com slices são len() e cap(). A primeira informa o tamanho do slice, e a segunda sua capacidade.

Note que, antes do append(), o tamanho do slice era 0. Após, mudou para 2, sem termos que explicitamente indicar isso.

Usando make()

Outra função embutida útil quando falamos em construir slices (mas não apenas eles!) é make(). Essa função cria uma instância do tipo que for passado como o primeiro argumento e define tamanho e capacidade de acordo com o segundo argumento. O terceiro argumento é opcional: se for passado, representará a capacidade - nesse caso, o segundo argumento será o tamanho, que pode ser diferente da capacidade mas nunca maior). Vamos ilustrar?

Sintaxe:

Dessa forma, podemos atribuir valores ao nosso slice diasDeSemana usando os índices sem receber o erro index out of range, da seguinte forma:

circle-info

Instância

Você pode ter percebido essa palavra no parágrafo acima. Daqui para frente a usaremos bastante, bem como alguns derivados: instanciar, instanciado.

Esse é um termo técnico usado para dizer que criamos uma cópia de um objeto de um tipo específico. Por exemplo, quando declaramos var nome []string estamos instanciando um objeto do tipo []string que terá o identificador nome.

Outro termo técnico que pode ser usado como sinônimo é alocação em memória.

Acessando subconjuntos do slice

É possível fatiar um slice para obter subconjuntos dele. Isso é feito usando : dentro do operador [ ]. Se nenhum índice for indicado, todos os elementos do slice original formarão a fatia. Nessa sintaxe, o primeiro parâmetro para definir um subconjunto é inclusivo, já o segundo é exclusivo (em notação matemática seria algo do tipo [índice:índice[). Alguns exemplos para ilustrar:

Rode o código abaixo na sua máquina para visualizar os subconjuntos:

Organização interna

<em breve>

Maps

Mapas são estruturas de dados que associam chaves e valores (também poderíamos dizer que representam tabelas hasharrow-up-right). Eles nos permitem mapear qualquer tipo de dado para qualquer tipo de dado. Quando falamos de map existem dois conceitos centrais: chave (key) e valor (value) onde, para cada chave - que é única - só existirá um valor associado.

Um exemplo de um “map” da vida real é um dicionário: as palavras são como chaves únicas que identificam os “valores” de seus significados.

Sintaxe:

Exemplo:

Usando make()

Também podemos usar a função embutida make() para instanciar um map. Diferentemente dos slices, não indicamos tamanho e capacidade, apenas o tipo do mapa.

Exemplo:

Atribuindo valores ao map

A atribuição de valores em maps é parecida com slices, a chave atua como se fosse um índice. Também similar ao slice, se tentarmos armazenar um par de chave e valor a um mapa que não foi inicializado (declarado apenas na forma var nome map[string]string), receberemos o erro assignment to entry in nil map em tempo de execução. Criar o mapa com make() evita essa situação.

Exemplo:

Mapa literal

Também é possível usar um mapa literal para instanciar um mapa e atribuir valores no momento da sua declaração. A sintaxe é a seguinte:

Exemplo:

Recuperando valores de um mapa

Para recuperar um valor do mapa, usamos sua chave.

Exemplo:

Checando se um par chave e valor existe

Quando formos resgatar um valor em um mapa, é possível verificar se a chave existe. A sintaxe é a seguinte:

Exemplo:

Deletando valores de um mapa

Para deletar valores de um mapa, podemos usar a função embutida delete(). Essa função recebe dois argumentos: o nome do mapa e a chave que identifica o par chave e valor que vai ser removido.

Exemplo:

Structs

Structs são estruturas de dados que agrupam um conjunto de campos de tipos definidos por quem os descreve. Permitem assim representar na aplicação o mundo real, e por isso são extremamente importantes. São muito usados em vários contextos diferentes, para modelar entidades, agregados e requisitos necessários para a aplicação.

Structs nos permitem representar não apenas dados jogados, como variáveis chamadas nome do tipo string e idade do tipo int, mas sim uma Pessoa com seus atributos nome e idade agregados.

Sintaxe:

Explorando a ideia da struct para representar uma pessoa: vamos criar uma struct do tipo Pessoa contendo os campos nome e idade.

Exemplo:

Instanciando uma struct

Como instanciamos uma “nova pessoa” registrando seus atributos nos campos da struct? O exemplo abaixo ilustra:

Acessando os campos

Os campos individuais são acessados usando ponto, como no exemplo abaixo.

Os campos podem receber novos valores, por exemplo: pessoa.Idade = 21

Estruturas podem também conter outras estruturas. No exemplo, Aula é uma struct que tem um campo do tipo string, um do tipo Pessoa e outro do tipo “slice de Pessoas”.

Exemplo:

Last updated