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:
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:
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:
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()
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()
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:
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
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()
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