O tipo struct é um tipo de dado complexo que é composto, de um ou mais membros, de tipos de dados diferentes como primitivos, ponteiros, funções e outros.

Tab. 1: Abstração de Struct
uint8 String float32
1 "tênis" 150,50

A sintaxe 1 e 2 abaixo mostram respectivamente a declaração/definição do tipo de dado e a declaração de uma variável do novo tipo de dado:

Sintaxe 1
//definição do tipo
type nome_tipo_struct struct
{
  membro1 tipo_dado1
  membro2 tipo_dado2
  membroN tipo_dadoN
};

O corpo de uma struct é delimitado por { e } tendo o uso de (;) para separar os membros como sendo opcional.

Sintaxe 2
var nome_var nome_tipo_struct //não iniciada
var nome_var nome_tipo_struct = nome_tipo_struct{} //iniciada com valor padrão
var nome_var nome_tipo_struct = nome_tipo_struct { membro1: valor1, ... , membroN: valorN} 
var nome_var nome_tipo_struct = nome_tipo_struct { valor1, ... , valorN} 
var nome_var nome_tipo_struct = nome_tipo_struct { valor1, ... , valorN} 
var nome_var = nome_tipo_struct { valor1, ... , valorN} 
nome_var := nome_tipo_struct { valor1, ... , valorN}

Respectivamente as sintaxes mostrar a declaração de variáveis do tipo struct como: não iniciadas, iniciadas com nome de membros e inciadas com valores e usando inferência.

Exemplo 1
package main

type produto struct {
	codigo int
	nome   string
	valor  float32
}

func main() {

	var novo_produto produto = produto{}

	var produto1 produto = produto{codigo: 1, nome: "prod1", valor: 1.99}
	var produto2 produto = produto{codigo: 2, nome: "prod2", valor: 1.99}
}
Saída
-

A linguagem Go também permite que a declaração dos membros de uma struct seja feita em apenas uma única linha, como pode ser visto no exemplo abaixo.

Exemplo 2
type minha_struct struct {
  codigo , id , tipo int 
}
Saída
-

O acesso aos membros de um struct é feita utilizando o operadore (.) seguido do nome do membro como pode ver visto na sintaxe abaixo:

Sintaxe: Alteração
variavel_struct.nome_membro = novo_valor
Sintaxe: Acesso
variavel_struct.nome_membro
Exemplo 3
package main

import "fmt"

type produto struct {
	codigo int
	nome   string
	valor  float32
}

func main() {

	var novo_produto produto = produto{}

	novo_produto.codigo = 1
	novo_produto.nome = "novo_produto"
	novo_produto.valor = 1.99

	fmt.Println(novo_produto)

	var valor float32 = novo_produto.valor
	nome := novo_produto.nome

	fmt.Println(valor)
	fmt.Println(nome)
}
            
Saída
{1 novo_produto 1.99}
1.99
novo_produto
            

A forma mais básica de passarmos uma struct como argumento para uma função é por valor. Lembrando que quando essa abordagem é utilizada, alterando-se o valor de um membro irá refletir na variável que foi passada como argumento.

Sintaxe: declaração
func nome_funcao(param nome_struct) { /*corpo*/}
Sintaxe: argumentos
nome_funcao(variavel_struct)
Exemplo 4
package main

import (
	"fmt"
)

type produto struct {
	codigo int
	nome   string
	valor  float32
}

func funcao(param produto) {
	fmt.Println(param)
}

func main() {

	var novo_produto produto = produto{}
	novo_produto.codigo = 1
	novo_produto.nome = "novo_produto"
	novo_produto.valor = 1.99

	funcao(novo_produto)
}
Saída
{1 novo_produto 1.99}

Para que seja possível a persistência de uma alteração feita em um struct, sendo essa acessada dentro de uma função ou método, é necessário fazer a passagem da struct como argumento por referência utilizando o operador (&).

Sintaxe: função
func nome_funcao( param *tipo_struct) { /*corpo*/}
Sintaxe: argumento
nome_funcao(&variavel_struct)
Exemplo 5
package main
import (
	"fmt"
)

type produto struct {
	codigo int
	nome   string
	valor  float32
}

func funcao(param *produto) {
	fmt.Println(param)
	param.codigo = 2
	(*param).nome = "tênis" //== param.nome
}

func main() {

	var novo_produto produto = produto{}
	novo_produto.codigo = 1
	novo_produto.nome = "novo_produto"
	novo_produto.valor = 1.99

	fmt.Println("antes:", novo_produto)
	funcao(&novo_produto)
	fmt.Println("depois:", novo_produto)
}
Saída
antes: {1 novo_produto 1.99}
&{1 novo_produto 1.99}
depois: {2 tênis 1.99}

O retorno de vetores em funções é feito da mesma forma como qualquer outro tipo de dado já visto. Declaramos o tipo de retorno no protótipo da função como um tipo struct.

Sintaxe: retorno struct
func nome_funcao(<lista_parametros>) ( nome_parametro nome_struct )

func nome_funcao( <lista_parametros>) ( nome_struct )
Exemplo 6
package main

import (
	"fmt"
)

type produto struct {
	codigo int
	nome   string
	valor  float32
}

func funcao() produto {

	var novo produto = produto{1, "teste", 1.99}
	return novo
}

func funcao2() (retorno produto) {
	retorno = produto{1, "teste", 1.99}
	return
}

func main() {

	prod1 := funcao()
	fmt.Println(prod1)

	prod2 := funcao2()
	fmt.Println(prod2)

}
            
Saída
{1 teste 1.99}
{1 teste 1.99}

O uso de structs como matrizes ou vetores é feito da mesma forma que outros tipos de dados. Definimos suas dimensões utilizando [ e ]. O acesso aos seus elementos é utilizando seus índices ou linha e colunas.

Sintaxe: declaração
var nome_vetor[<dimensao>] nome_struct
Sintaxe: atribuição
var nome_vetor = [<dimensao>] nome_struct { <lista_elementos> }

var nome_matriz = [<linhas>][<colunas>] nome_struct { {<lista_elementos> }, //linha 1
                                                      {<lista_elementos>} , //linha 2 
                                                      {<lista_elementos>}}  //Linha N
//inferência
Onde
<dimensao>: número de elementos do array
<linhas/colunas>: dimensões da matriz

O acesso aos membros de structs contidos em vetores e matrizes podems ser acessados como na sintaxe abaixo:

Sintaxe: acessando membro
nome_vetor[indice].nome_membro = valor

nome_matriz[linha][coluna].nome_membro = valor
nome_vetor[indice].nome_membro

nome_matriz[linha][coluna].nome_membro

Ao recuperarmos um elementos do tipo struct contido em um vetor ou matriz e posterior alteração de seus membros não irá refletir no elemento original pois uma cópia foi realizada.

Exemplo 7
package main

import "fmt"

type produto struct {
	codigo int
	nome   string
	valor  float32
}

func main() {

	var produto1 produto = produto{codigo: 1, nome: "prod1", valor: 1.99}
	var produto2 produto = produto{codigo: 2, nome: "prod2", valor: 1.99}

	lista := [2]produto{produto1, produto2}
	matriz := [2][2]produto{{produto1, produto2}, {produto1, produto2}}

	lista[0].nome = "produto1"
	fmt.Println(lista)

	matriz[0][0].nome = "produto2"
	fmt.Println(matriz)
}
Saída
[{1 produto1 1.99} {2 prod2 1.99}]
[[{1 produto2 1.99} {2 prod2 1.99}] [{1 prod1 1.99} {2 prod2 1.99}]]

Para iterar elementos de matrizes e vetores os comandos de repetição devem utilizados. Os comandos de repetição foram vistos em Go: Comandos de repetição.

Exemplo 8
package main

import "fmt"

type produto struct {
	codigo int
	nome   string
	valor  float32
}

func main() {

	var produto1 produto = produto{codigo: 1, nome: "prod1", valor: 1.99}
	var produto2 produto = produto{codigo: 2, nome: "prod2", valor: 1.99}

	lista := [2]produto{produto1, produto2}

	for indice, prod := range lista {
		fmt.Println(indice, prod)
	}
}
Saída
0 {1 prod1 1.99}
1 {2 prod2 1.99}

A linguagem Go, não fornece nativamente uma função para que seja possível realizar a cópia de vetores, mas podemos copiar os elementos de um vetor utilizando o operador (:=).

Sintaxe: cópia
var var_struct_destino nome_struct = var_struct_origem

var_struct_destino := var_struct_origem
Exemplo 9
package main

import "fmt"

type produto struct {
	codigo int
	nome   string
	valor  float32
}

func main() {

	var prod1 produto = produto{codigo: 1, nome: "prod1", valor: 1.99}

	fmt.Println(prod1)
	copia := prod1
	copia.codigo = 2
	fmt.Println(copia)

}
Saída
{1 prod1 1.99}
{2 prod1 1.99}

A linguagem Go permite que possamos comparar o conteúdo de structs utilizando os operadore == e !=, eliminando assim a necessidade de verificação manual de cada membro das structs para fazer a cópia.

Sintaxe: comparação
variavel_struct1 == variavel_struct2
variavel_struct1 != variavel_struct2
Exemplo 10
package main

import "fmt"

type produto struct {
	codigo int
	nome   string
	valor  float32
}

func main() {

	var prod1 produto = produto{codigo: 1, nome: "prod1", valor: 1.99}
	var prod2 produto = produto{codigo: 1, nome: "prod1", valor: 1.99}
	var prod3 produto = produto{codigo: 2, nome: "prod1", valor: 1.99}

	fmt.Println(prod1 == prod2)
	fmt.Println(prod1 != prod3)

}
Saída
true
true

Para comparações mais complexas que envolvem tipos de dados como ponteiros ou structs aninhadas é necessário criar métodos específicos para o tipo de dado em questão.

Go permite também uma função possa fazer seja membro de uma struct. Assim, podemos adicionar comportamentos em nossas structs associando um grupo de funcionalidades a um tipo de dado.

Sintaxe
type nome_struct struct {
    nome_membro func( <lista_parametros>) tipo_retorno
}

O acesso ou invocação da função como membro é feita utilizando o operador de acesso (.) seguido do nome da função:

Ex
package main
import "fmt"

type objeto struct{
     funcao1 func(p string)
     funcao2 func(p1 int , p2 int) int
}

func funcao(p string){
    fmt.Println(p);
}

func funcao2(p1 int, p2 int) int {
    return p1 + p2;
}

func main() {
  obj := objeto{ funcao, funcao2}
  obj.funcao1("teste")
  fmt.Println(obj.funcao2(1,2));
}
Saída
teste
3

Para mais informações sobre funções em Go, visite a seção Funções.

Uma struct anônima a uma struct que não possuem nomes para seus membros, sendo apenas informados seus tipos.

Sintaxe
type nome_struct {
tipo_dado1
tipo_dado2
tipo_dadoN
}

O corpo de uma struct é delimitado por { e } tendo o uso de (;) para separar os membros anônimos como sendo opcional.

Como consequência da não declaração de seus membros, esses são acessados pelos seus tipos:

Sintaxe
var_struct.tipo_dado1
var_struct.tipo_dado2
var_struct.tipo_dadoN
Ex
type msa struct {
 int
 string
}

package main
import "fmt"

type struct_anonima struct {
    int     
    string
}

func main() {
    msa := struct_anonima {1, "Teste"}

    fmt.Println("codigo:", msa.int)
    fmt.Println("descricao:", msa.string)
}
Saída
codigo: 1
descricao: Teste

Os membros de uma struct anônima devem ser distintos, podendo ser declarados com ou sem nome do membro:

Sintaxe
type nome_struct struct {
    tipo_dado1
    nome_campo tipo_dado2
}
Ex
package main
import "fmt"

type msa struct {
    int
    nome string
}
func main() {
  teste := msa{1,"Teste"}
  fmt.Println(teste.int);
  fmt.Println(teste.nome);
}
Saída
1
Teste

Go permite que nossas structs possam ser compostas de outras structs. Assim, podemos reusar campos de structs em novas structs reduzindo repetição de código e adicionando segregando structs, por exemplo.

Sintaxe
type nome_struct1 struct {
membro1
membro2
membroN
}

type nome_struct2 struct
{
  nome_membro1 nome_struct1
  nome_membro2 tipo_dado2
  nome_membroN tipo_dadoN
}
Ex
package main
import "fmt"

type cod struct{
    int
}
type desc struct{
    string
}

type objeto struct{
     codigo cod //
     descricao desc
     flag bool
}

func main() {
  obj := objeto{ cod{ 1 }, desc{"teste"}, false}
  fmt.Println(obj.codigo.int);
  fmt.Println(obj.descricao.string);
  fmt.Println(obj.flag);
  fmt.Println(obj);
}
Saída
1
teste
false
{{1} {teste} false}

O tamanho de uma struct está ligado diretamente aos tamanhos dos dados usados por seus membros e também o alinhamento de bytes utilizado por eles.

Ex A
type s1 struct
{
   bool,  //1 byte  + 7 de alinhamento
   int64  //8 bytes
}
Tab. 1: alinhamento
Byte 1 Byte 2 Byte 3 Byte 4 Byte 5 Byte 6 Byte 7 Byte 8
bool - - - - - - -
int64
Ex B
type s1 struct
{
   bool,  //1 byte  + 7 de alinhamento
   int32  //8 bytes
}
Tab. 2: alinhamento
Byte 1 Byte 2 Byte 3 Byte 4
bool - - -
int32

Com base no tipo de dado com maior quantidade de armazenamento, exceto string e outros tipos complexos não testados, os tipos com menores quantidades de bytes sofrem um alinhamento. Esse, impacta diretamente no tamanho total de uma struct.

Para termos conhecimento do tamanho de uma struct, devemos importar o package unsafe e fazer uso da função Sizeof:

Sintaxe
import "unsafe"
Sintaxe
unsafe.Sizeof( var_struct )
Ex
package main
import "fmt"
import "unsafe"

type s1 struct{
     bool
     int32
}

type s2 struct{
     bool
     int64
}

func main() {
  obj1 := s1{ false,1}
  obj2 := s2{ false,1}
  fmt.Println(unsafe.Sizeof(obj1));
  fmt.Println(unsafe.Sizeof(obj2));
}
Saída
8
16
  1. 09/09/2025 - revisão 4 - Ajustes: pontuais, target de links, objetivos e sintaxes
  2. 08/04/2025 - revisão 3 - struct: anônima, função como membro, composta e tamanho
  3. 17/09/2024 - revisão 2 - Correção em Sintaxes, número de exemplos/sintaxes e saídas dos Ex. 4/9; ajustes gramaticais
  4. 02/10/2023 - revisão 1 - Correção em referências, erros gramaticais
  5. 21/08/2023 - versão inicial