Skip to main content


Structs


Uma struct é uma estrutura usada para agrupar dados relacionados em um único tipo. Até agora, quando queríamos guardar dados de uma pessoa, poderíamos fazer assim:

nome := "Ana"
idade := 20
email := "ana@email.com"

Isso funciona, mas os valores ficam soltos. Uma struct permite juntar esses dados em uma única estrutura:

type Usuario struct {
Nome string
Idade int
Email string
}

Agora Usuario representa um tipo próprio (o tipo é Usuario), com três campos.



Por que structs existem


As Structs existem para representar dados com formato conhecido. Por exemplo, um usuário tem nome, idade e email. Um produto tem nome, preço e estoque. Um pedido tem código, valor e status.


Até poderíamos usar map[string]any para isso:

usuario := map[string]any{
"nome": "Ana",
"idade": 20,
"email": "ana@email.com",
}

Mas isso perde segurança de tipo, o Go não sabe com clareza quais campos existem e quais tipos cada campo deveria ter. Com struct, o formato fica explícito:

type Usuario struct {
Nome string
Idade int
Email string
}

Agora o Go sabe que Nome é string, Idade é int e Email é string.



Criando uma struct


Primeiro declaramos o tipo:

type Usuario struct {
Nome string
Idade int
Email string
}

Exemplo completo:

package main

import "fmt"

// Usuario representa os dados de um usuário.
type Usuario struct {
Nome string
Idade int
Email string
}

func main() {
// Cria um valor do tipo Usuario.
usuario := Usuario{
Nome: "Ana",
Idade: 20,
Email: "ana@email.com",
}

fmt.Println(usuario)
}

Resultado:

{Ana 20 ana@email.com}


Struct declarada dentro e fora da função


O mais comum é declarar uma struct fora de função, quando ela representa algo importante no programa.

package main

import "fmt"

// Usuario é um tipo criado no nível do pacote.
// Pode ser usado por qualquer função dentro deste pacote.
type Usuario struct {
Nome string
Idade int
}

func main() {
usuario := Usuario{
Nome: "Ana",
Idade: 20,
}

fmt.Println(usuario)
}

Como ele foi declarado fora de main, outras funções do mesmo pacote também podem usar esse tipo.

func mostrarUsuario(usuario Usuario) {
fmt.Println(usuario.Nome)
}

Essa forma é melhor quando a struct será reutilizada. Mas também é possível declarar uma struct dentro de uma função.

package main

import "fmt"

func main() {
// Tipo Usuario criado apenas dentro da função main.
type Usuario struct {
Nome string
Idade int
}

usuario := Usuario{
Nome: "Ana",
Idade: 20,
}

fmt.Println(usuario)
}

Isso funciona, mas o tipo Usuario só existe dentro da função main, fora dela, esse tipo não pode ser usado.



Struct anônima dentro da função


Também podemos criar uma struct sem nome, direto na variável.

package main

import "fmt"

func main() {
usuario := struct {
Nome string
Idade int
}{
Nome: "Ana",
Idade: 20,
}

fmt.Println(usuario)
}

Isso é útil para casos pequenos e temporários, quando não vale a pena criar um tipo com nome.



Acessando campos da struct


Para acessar um campo, usamos ponto ..

usuario.Nome
usuario.Idade
usuario.Email

Exemplo:

package main

import "fmt"

type Usuario struct {
Nome string
Idade int
Email string
}

func main() {
usuario := Usuario{
Nome: "Ana",
Idade: 20,
Email: "ana@email.com",
}

// Acessa campos específicos da struct.
fmt.Println(usuario.Nome)
fmt.Println(usuario.Idade)
fmt.Println(usuario.Email)
}

Resultado:

Ana
20
ana@email.com


Alterando campos da struct


Campos de uma struct podem ser alterados usando =.

package main

import "fmt"

type Usuario struct {
Nome string
Idade int
Email string
}

func main() {
usuario := Usuario{
Nome: "Ana",
Idade: 20,
Email: "ana@email.com",
}

// Altera o campo Idade.
usuario.Idade = 21

fmt.Println(usuario.Idade)
}

Resultado:

21

A ideia é parecida com alterar uma variável, só que agora estamos alterando um campo dentro da struct.



Valor zero em structs


Se criarmos uma struct sem preencher os campos, cada campo recebe o valor zero do próprio tipo.

package main

import "fmt"

type Usuario struct {
Nome string
Idade int
Ativo bool
}

func main() {
var usuario Usuario

fmt.Println(usuario.Nome) // string vazia
fmt.Println(usuario.Idade) // 0
fmt.Println(usuario.Ativo) // false
}

Valores zero usados nesse exemplo:

string -> ""
int -> 0
bool -> false


Criando struct com alguns campos


Não é obrigatório preencher todos os campos ao criar a struct.

package main

import "fmt"

type Usuario struct {
Nome string
Idade int
Email string
}

func main() {
usuario := Usuario{
Nome: "Ana",
}

fmt.Println(usuario.Nome)
fmt.Println(usuario.Idade)
fmt.Println(usuario.Email)
}

Resultado:

Ana
0


A Idade ficou 0, o Email ficou com string vazia. Isso pode ser útil, mas também exige cuidado, às vezes um campo vazio pode indicar que faltou preencher algum dado importante.



Criando struct sem nome dos campos


O Go permite criar uma struct passando os valores na ordem dos campos.

usuario := Usuario{"Ana", 20, "ana@email.com"}

Isso funciona, mas normalmente é melhor evitar, sempre prefira informar o nome dos campos:

usuario := Usuario{
Nome: "Ana",
Idade: 20,
Email: "ana@email.com",
}

Com os nomes dos campos, o código fica mais claro e menos frágil. Se a struct mudar de ordem depois, isso pode dar problema.



Struct dentro de slice


É comum ter uma lista de structs.

package main

import "fmt"

type Usuario struct {
Nome string
Idade int
}

func main() {
usuarios := []Usuario{
{Nome: "Ana", Idade: 20},
{Nome: "Carlos", Idade: 25},
{Nome: "Bia", Idade: 18},
}

for _, usuario := range usuarios {
fmt.Println(usuario.Nome, usuario.Idade)
}
}

Resultado:

Ana 20
Carlos 25
Bia 18

Esse formato aparece muito em APIs, bancos de dados e listas de resultados.



Struct vs Map


Use struct quando os campos são conhecidos.

type Usuario struct {
Nome string
Idade int
}

Use map quando as chaves são mais dinâmicas.

config := map[string]string{
"ambiente": "dev",
"porta": "8080",
}

Um map[string]any até permite valores variados:

usuario := map[string]any{
"nome": "Ana",
"idade": 20,
}

Mas para dados com formato fixo, struct é melhor. Com struct, o Go consegue verificar os tipos antes do programa rodar.



Campos com letra maiúscula e minúscula


Em Go, o nome do campo também importa para visibilidade, campos com letra maiúscula podem ser acessados de outros pacotes.

type Usuario struct {
Nome string
}

Campos com letra minúscula ficam acessíveis apenas dentro do mesmo pacote.

type Usuario struct {
nome string
}

Como ainda estamos trabalhando em um único arquivo com package main, isso não pesa tanto agora. Mais tarde, quando estudarmos packages, essa diferença vai ficar mais importante.


Por enquanto, nos exemplos, vamos usar campos com letra maiúscula:

type Usuario struct {
Nome string
Idade int
}