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
//definição do tipo
type nome_tipo_struct struct 
{
  membro1 tipo_dado1[;]
  membro2 tipo_dado1[;]
  membroN tipo_dadoN[;]
};

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

Sintaxe
var nome_variavel nome_tipo_struct //não iniciada
var nome_variavel [nome_tipo_struct] = nome_tipo_struct {} //não iniciada
var nome_variavel [nome_tipo_struct] = nome_tipo_struct { membro1: valor1, membro2:valor2 , membroN: valorN} 
var nome_variavel [nome_tipo_struct] = nome_tipo_struct { valor1, valor2 , valorN} 

As sintaxes acima dizem respeito à declaração de variáveis do tipo struct não iniciadas, iniciadas com nome de membros e inciados sem nomes de membros, respectivamente.

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
var nome_variavel tipo_dado = variavel_struct.nome_membro
nome_variavel := 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 da função
func nome_funcao(param nome_struct) (tipo_retorno){ /*corpo*/}
Sintaxe: passagem de argumento
nome_funcao(nome_variavel)

As sintaxe 5 e 6 acima mostram a declaração de um protótipo de uma função e a passagem de uma struct como argumento respectivamente.

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: declaração da função
func nome_funcao(param *tipo_dado, tamanho) (tipo_retorno){ /*corpo*/}
Sintaxe: passagem de argumento
nome_funcao(&nome_vetor)

As sintaxe 7 e 8 acima mostram a declaração de um protótipo de uma função e a passagem de uma struct como argumento respectivamente.

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(parametros) (tipo_struct nome_struct)
func nome_funcao(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 { elemento1, elemento2, elementoN}
var nome_matriz = [linhas][colunas] nome_struct { {el1, el2 , elN }, //linha 1
                                               {el3, el4 , elN} , //linha 2 
                                               {elM, elM, elM}    //Linha N
Onde
dimensao: número de elementos
elemento1...N: Elementos do tipo tipo_dado que iniciam nome_vetor
el1...N: Elementos do tipo tipo_dado que iniciam nome_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 elementos 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, Go: Vetores e Go: Matrizes.

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 elemento de um vetor utilizando o operador (:=).

Sintaxe: cópia
variavel_struct_destino := variavel_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 o comparador de igualdade "==", 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
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
false

Go, além de membros anônimos e compostas, permite também que uma função possa fazer parte de um 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(parâmetros) 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
codigo: 1
descricao: Teste

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

A linguagem Go permite que nossas structs possam ser compostas de outras structs. Assim, podemos reusar campos de structs em novas structs eliminando repetição de código e 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 aos tamanhos dos dados de seus membros e também o alinhamento de memória.

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 para 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 como observado nos exemplos acima.

Para termos conhecimento do tamanho de uma struct, devemos importar o package unsafe e fazer uso do método 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. 08/04/2025 - revisão 3 - struct: anônima, função como membro, composta e tamanho
  2. 17/09/2024 - revisão 2 - Correção em Sintaxes, número de exemplos/sintaxes e saídas dos Ex. 4/9; ajustes gramaticais
  3. 02/10/2023 - revisão 1 - Correção em referências, erros gramaticais
  4. 21/08/2023 - versão inicial