Structs em Go: Guia Completo com Exemplos Práticos

Foto do autor

By luis

Em Go, as estruturas (structs) são mecanismos fundamentais para a criação de tipos de dados personalizados, sendo amplamente utilizadas no desenvolvimento de aplicações.

Neste guia, exploraremos todos os aspectos essenciais das structs e demonstraremos como empregá-las em seus projetos Go, através de exemplos práticos.

Vamos começar esta jornada!

Introdução

Uma estrutura é, em essência, um conjunto de variáveis (denominadas campos), cada qual com seu tipo de dado específico, agrupadas em uma única unidade. Elas oferecem um método eficaz para organizar dados relacionados, formando registros customizados. Uma struct pode incluir tanto tipos primitivos quanto outros tipos definidos pelo usuário, sendo ela própria um tipo definido pelo usuário.

As structs em Go são entidades mutáveis, o que significa que seus valores podem ser alterados durante a execução do programa.

A utilização de structs contribui para a melhoria da arquitetura geral do código, permitindo a criação e manipulação de estruturas de dados complexas em diferentes partes do sistema. Por exemplo, ao invés de passar um grande número de parâmetros individualmente para uma função, o que torna o código mais confuso, pode-se passar uma única struct, que contém todos os dados necessários. Isso simplifica o código e facilita sua manutenção.

A declaração de uma struct utiliza duas palavras-chave: type e struct. Os campos são declarados entre chaves (de forma similar às classes em Java), cada campo com seu nome (identificador) e seu respectivo tipo de dado. Detalharemos a implementação na próxima seção.

Para quem possui familiaridade com Programação Orientada a Objetos (POO), é possível pensar nas structs como classes, porém sem o conceito de herança.

Definindo Estruturas

Agora que você já tem uma compreensão do conceito de structs e suas aplicações, vamos aprender a como declarar uma struct. A sintaxe básica de uma struct é:

type NomeDaStruct struct {
     campo1 TipoDoCampo1
     campo2 TipoDoCampo2
 }

Nesta declaração, as palavras-chave type e struct são reservadas, e a estrutura contém diversos campos, cada um com seu tipo de dado específico.

Vejamos um exemplo prático:

package main

import (
	"fmt"
)

type Usuario struct {
	nome        string
	idade         int
	saldoBancario float32
}

func main() {
	var usuario Usuario
	fmt.Println(usuario)
}

Neste exemplo, criamos uma struct chamada Usuario, composta pelos campos nome (string), idade (int) e saldoBancario (float32). Na função main(), declaramos uma variável do tipo Usuario e a imprimimos. O resultado é o valor inicial/vazio da struct, uma vez que ainda não foi atribuído nenhum valor aos campos. O valor zero corresponde ao valor padrão de cada campo (zero para inteiros, strings vazias, etc).

{ 0 0}

Atribuindo Valores a Estruturas

Na seção anterior, aprendemos a declarar structs. Agora, vamos ver como inicializar ou atribuir valores aos seus campos. Observe o código abaixo:

package main

import (
	"fmt"
)

type Usuario struct {
	nome        string
	idade         int
	saldoBancario float32
}

func main() {
	// Com nomes de campos
	usuario1 := Usuario{
		nome:        "Pedro",
		idade:         24,
		saldoBancario: 100.0,
	}
	
	// Sem nomes de campos
	usuario2 := Usuario{"Maria", 21, 1000.0}
	
	fmt.Println(usuario1)
	fmt.Println(usuario2)
}

O código demonstra como inicializar structs, tanto com a nomeação dos campos quanto sem ela. O resultado da execução será:

{Pedro 24 100}
{Maria 21 1000}

Caso algum campo não seja inicializado, ele receberá o valor padrão (valor zero) do seu tipo.

usuario1 := Usuario{
	nome:        "Pedro",
	idade:         24,
}

// Saída: {Pedro 24 0.0}

Existe ainda outra forma de criar structs, utilizando a palavra-chave new. Veremos como usá-la na próxima seção.

Acessando os Campos de uma Struct

Agora que aprendemos a criar e inicializar structs, vamos explorar como acessar seus campos. Em Go, utilizamos o operador ponto para isso. Com base no exemplo anterior, vamos acessar e imprimir os campos nome e idade:

package main

import (
	"fmt"
)

type Usuario struct {
	nome        string
	idade         int
	saldoBancario float32
}

func main() {
	// Com nomes de campos
	usuario := Usuario{
		nome:        "Pedro",
		idade:         24,
		saldoBancario: 100.0,
	}

	fmt.Println(usuario.nome)
	fmt.Println(usuario.idade)
	fmt.Println(usuario.saldoBancario)
}

Aqui, utilizamos a sintaxe usuario.nome_do_campo para acessar os campos da estrutura. A saída do código acima será:

Pedro
24
100

Como mencionado anteriormente, structs podem ser criadas também com a palavra-chave new. Veja como:

usuario := new(Usuario)
usuario.nome = "Pedro"
usuario.idade = 24
usuario.saldoBancario = 100.0

fmt.Println(usuario)

// Saída: &{Pedro 24 100}

A palavra-chave new retorna um ponteiro para a estrutura recém-criada. Em Go, não é necessário desreferenciar explicitamente o ponteiro (mas fmt.Println(*usuario) resultaria na mesma saída).

Estruturas Aninhadas

Em Go, estruturas podem conter outros tipos definidos pelo usuário, ou seja, uma estrutura pode conter outras estruturas aninhadas.

package main

import (
	"fmt"
)

type Usuario struct {
	nome        string
	idade         int
	saldoBancario float32
	detalhesCargo DetalhesCargo
}

type DetalhesCargo struct {
	posicao string
	equipe     string
}

func main() {
	detalhesCargoDePedro := DetalhesCargo{
		posicao: "Engenheiro de Software",
		equipe:     "Transporte",
	}
	usuario := Usuario{
		nome:        "Pedro",
		idade:         24,
		saldoBancario: 100.0,
		detalhesCargo: detalhesCargoDePedro,
	}

	fmt.Println(usuario)
}

Neste código, temos a struct DetalhesCargo como um campo da struct Usuario. A saída será:

{Pedro 24 100 {Engenheiro de Software Transporte}}

Para acessar os campos da struct aninhada, utilizamos a mesma sintaxe com o operador ponto, por exemplo:
usuario.detalhesCargo.posicao.

Comparação de Structs

Duas structs são consideradas iguais se todos os seus campos forem iguais (tanto os tipos primitivos quanto os definidos pelo usuário), contudo, nem todos os tipos de dados são comparáveis (mapas, por exemplo, não podem ser comparados diretamente). O exemplo a seguir demonstra como comparar structs:

package main

import (
	"fmt"
)

type Usuario struct {
	nome        string
	idade         int
	saldoBancario float32
}

func main() {
	usuario1 := Usuario{
		nome:        "Pedro",
		idade:         24,
		saldoBancario: 100.0,
	}
	usuario2 := Usuario{
		nome:        "Pedro",
		idade:         24,
		saldoBancario: 100.0,
	}
	usuario3 := Usuario{
		nome:        "Maria",
		idade:         21,
		saldoBancario: 1000.0,
	}

	if usuario1 == usuario2 {
		fmt.Println("usuario1 e usuario2 são iguais")
	} else {
		fmt.Println("usuario1 e usuario2 não são iguais")
	}

	if usuario1 == usuario3 {
		fmt.Println("usuario1 e usuario3 são iguais")
	} else {
		fmt.Println("usuario1 e usuario3 não são iguais")
	}
}

Structs vazias e com valor zero são consideradas iguais. A ordem dos campos não importa, o que importa é que cada campo correspondente seja igual. A saída do código acima será:

usuario1 e usuario2 são iguais
usuario1 e usuario3 não são iguais

Conclusão

Parabéns!

Agora você possui o conhecimento necessário para utilizar structs em Go. Abordamos todos os aspectos essenciais, desde a declaração e inicialização até o acesso aos campos e comparação de structs. Também exploramos o conceito de estruturas aninhadas. Aqui estão alguns recursos adicionais para aprofundar seu conhecimento sobre structs:

Há muito mais a aprender sobre structs, mas este é um excelente ponto de partida. Esperamos que você tenha aprendido algo novo!

Continue explorando. Continue aprendendo!