O que é Struct em Go?

Uma struct (estrutura) em Go é um tipo composto que agrupa campos nomeados sob um único tipo. Structs são a principal forma de modelar dados e entidades em Go, cumprindo o papel que classes ocupam em linguagens orientadas a objetos como Java ou Python — porém sem herança.

Em Go, não existem classes. Structs combinadas com métodos e interfaces formam o sistema de tipos da linguagem, usando composição em vez de herança para reutilização de código.

Declaração básica

type Usuario struct {
    Nome     string
    Email    string
    Idade    int
    Ativo    bool
}

Criando instâncias

Existem várias formas de criar uma struct em Go:

// Campos nomeados (recomendado)
u1 := Usuario{
    Nome:  "Maria",
    Email: "maria@email.com",
    Idade: 28,
    Ativo: true,
}

// Campos posicionais (frágil, evite)
u2 := Usuario{"João", "joao@email.com", 30, true}

// Valor zero — todos os campos com valor padrão
var u3 Usuario // Nome: "", Email: "", Idade: 0, Ativo: false

// Ponteiro com new
u4 := new(Usuario) // retorna *Usuario com valores zero

// Ponteiro com literal
u5 := &Usuario{Nome: "Ana", Email: "ana@email.com"}

A forma com campos nomeados é preferida porque é mais legível e não quebra quando novos campos são adicionados à struct.

Métodos em structs

Em Go, métodos são funções associadas a um tipo via receiver. Isso permite dar comportamento às structs:

type Retangulo struct {
    Largura float64
    Altura  float64
}

// Método com receiver de valor
func (r Retangulo) Area() float64 {
    return r.Largura * r.Altura
}

// Método com receiver de ponteiro (pode modificar a struct)
func (r *Retangulo) Redimensionar(fator float64) {
    r.Largura *= fator
    r.Altura *= fator
}

func main() {
    ret := Retangulo{Largura: 10, Altura: 5}
    fmt.Println("Área:", ret.Area()) // Área: 50

    ret.Redimensionar(2)
    fmt.Println("Área:", ret.Area()) // Área: 200
}

Quando usar ponteiro vs valor no receiver

Use receiver de ponteiro (*T)Use receiver de valor (T)
O método modifica a structA struct é pequena e imutável
A struct é grande (evita cópia)Consistência se outros métodos usam valor
Precisa de consistência (se algum método usa ponteiro, use em todos)Tipos básicos como time.Time

A regra prática: na dúvida, use ponteiro.

Embedding (composição)

Go não tem herança, mas oferece embedding — incorporar um tipo dentro de outro para promover seus campos e métodos:

type Endereco struct {
    Rua    string
    Cidade string
    Estado string
    CEP    string
}

type Funcionario struct {
    Nome     string
    Cargo    string
    Endereco // embedded — campos promovidos
}

func main() {
    f := Funcionario{
        Nome:  "Carlos",
        Cargo: "Desenvolvedor Go",
        Endereco: Endereco{
            Rua:    "Rua Augusta, 123",
            Cidade: "São Paulo",
            Estado: "SP",
            CEP:    "01305-000",
        },
    }

    // Campos do Endereco acessíveis diretamente
    fmt.Println(f.Cidade) // São Paulo
    fmt.Println(f.Endereco.Cidade) // também funciona
}

Embedding também funciona com interfaces, e é o principal mecanismo de composição em Go. Quando uma struct incorpora uma interface, ela automaticamente satisfaz aquela interface (delegando para a implementação embutida).

Tags de struct

Tags são metadados anexados aos campos de uma struct, usados por pacotes como encoding/json, encoding/xml, ORMs e validadores:

type Produto struct {
    ID        int     `json:"id" db:"product_id"`
    Nome      string  `json:"nome" validate:"required,min=3"`
    Preco     float64 `json:"preco" validate:"required,gt=0"`
    Descricao string  `json:"descricao,omitempty"`
    Interno   string  `json:"-"` // ignorado no JSON
}

Tags comuns:

  • json:"campo" — nome no JSON
  • json:",omitempty" — omite se valor zero
  • json:"-" — ignora o campo
  • db:"coluna" — usado por ORMs e query builders como sqlc
  • validate:"regra" — usado por bibliotecas de validação

Padrão construtor

Go não tem construtores como Java ou Python. A convenção é criar funções New* que retornam a struct inicializada:

type Conexao struct {
    host    string
    porta   int
    timeout time.Duration
    ativa   bool
}

func NovaConexao(host string, porta int) *Conexao {
    return &Conexao{
        host:    host,
        porta:   porta,
        timeout: 30 * time.Second, // valor padrão
        ativa:   false,
    }
}

Options pattern (funcional)

Para structs com muitas configurações opcionais, o padrão de opções funcionais é idiomático em Go:

type Opcao func(*Servidor)

type Servidor struct {
    host    string
    porta   int
    timeout time.Duration
    tls     bool
}

func ComTimeout(t time.Duration) Opcao {
    return func(s *Servidor) { s.timeout = t }
}

func ComTLS() Opcao {
    return func(s *Servidor) { s.tls = true }
}

func NovoServidor(host string, porta int, opcoes ...Opcao) *Servidor {
    s := &Servidor{
        host:    host,
        porta:   porta,
        timeout: 30 * time.Second,
    }
    for _, opt := range opcoes {
        opt(s)
    }
    return s
}

// Uso
srv := NovoServidor("localhost", 8080, ComTimeout(60*time.Second), ComTLS())

Structs e JSON

A interação entre structs e JSON é uma das operações mais comuns em Go:

type Resposta struct {
    Status  int      `json:"status"`
    Dados   []string `json:"dados"`
    Total   int      `json:"total"`
}

// Struct para JSON
resp := Resposta{Status: 200, Dados: []string{"Go", "Rust"}, Total: 2}
bytes, _ := json.Marshal(resp)
fmt.Println(string(bytes))
// {"status":200,"dados":["Go","Rust"],"total":2}

// JSON para struct
var resp2 Resposta
json.Unmarshal(bytes, &resp2)

Para uso avançado de JSON em Go, veja o artigo sobre a nova API experimental de JSON.

Perguntas Frequentes

Struct é como uma classe?

Structs em Go cumprem um papel semelhante ao de classes, mas com diferenças importantes. Não existe herança — Go usa composição via embedding. Não existem construtores — usa-se funções New*. Não existe modificador de acesso como private/public — a visibilidade é controlada pela capitalização do nome (maiúscula = exportado, minúscula = privado ao pacote). Structs combinadas com métodos e interfaces formam a base do sistema de tipos de Go.

Quando usar ponteiro para struct?

Use ponteiro (*Struct) quando a struct é grande (para evitar cópia), quando precisa modificar a struct em métodos, ou quando precisa representar ausência de valor (ponteiro nil). Use valor quando a struct é pequena e imutável, como time.Time. Na prática, a maioria das structs em Go é usada como ponteiro.

Como comparo duas structs?

Structs são comparáveis com == se todos os seus campos forem comparáveis (tipos como int, string, bool). Structs com campos de slice, map ou função não podem ser comparadas com == — use reflect.DeepEqual ou compare campo a campo. A partir do Go 1.18, você também pode usar generics para criar funções de comparação reutilizáveis.

Posso ter métodos em tipos que não são struct?

Sim. Em Go, você pode definir métodos em qualquer tipo nomeado que você declare no mesmo pacote, não apenas structs. Por exemplo, type Celsius float64 pode ter métodos. A única restrição é que o tipo deve ser definido no mesmo pacote onde os métodos são declarados.

Veja também