Caso você deseje utilizar esse conteúdo como uma fonte de estudos ao invés de uma revisão, eu sugiro acompanhar o repositório original, aonde estarão sendo manutenções e melhorias de acordo com as necessidades, além de claro, estar separado por tópico em uma leitura menos cansativa.
Vamos ao que interessa.
1. Introdução a Go
Go, também conhecida como Golang, é uma linguagem de programação de código aberto desenvolvida pelo Google. Foi projetada para ser eficiente, concisa, simples e fácil de ler. O projeto inicial da lingaugem de programação Go foi iniciado em setembro de 2007 por três engenheiros renomados: Robert Griesemer, Rob Pike e Ken Thompson, que trabalhavam no Google. Eles colaboraram para criar uma linguagem que atendesse às necessidades específicas de desenvolvimento de software no ambiento do Google, enfocando eficiência, concisão e legibilidade do código. A primeira versão estável foi lançada em 2012. Algumas características notáveis da linguagem Go incluem:
Concorrência e Paralelismo:
Go foi projetada com suporte integrado para concorrência e paralelismo. Ela possui primitivas de concorrência, como goroutines (unidades leves de execução) e canais (mecanismo de comunicação entre goroutines), facilitando a construção de programas concorrentes de maneira eficiente.
Coleta de Lixo Eficiente:
Go possui um coletor de lixo eficiente que gerencia automaticamente a alocação e liberação de memória, aliviando os desenvolvedores da responsabilidade de gerenciar manualmente a alocação e desalocação de memória.
Simplicidade e Legibilidade:
Go foi projetada com uma sintaxe simples e clara. A linguagem evita recursos complexos e excessivamente abstratos, o que facilita a leitura e manutenção do código. A ideia é que o código seja claro e compreensível sem a necessidade de muita explicação.
Compilação Rápida:
Go é uma linguagem compilada e a compilação é rápida, o que ajuda no desenvolvimento eficiente. Ela também suporta a compilação cruzada, permitindo que você compile o código para diferentes sistemas operacionais e arquiteturas.
Gerenciamento de Dependências Integrado:
O Go possui uma ferramenta integrada para gerenciamento de dependências chamada "go modules". Isso facilita o controle e a gestão das dependências do projeto.
Orientação a Interfaces:
Go segue um paradigma de programação orientada a interfaces. Em vez de depender de herança de classes, o Go incentiva a composição de tipos através de interfaces, proporcionando flexibilidade e reutilização de código.
Ferramentas de Teste Integradas:
Go possui um sistema de teste integrado, facilitando a escrita e execução de testes unitários e de integração.
Suporte a Programação de Baixo Nível:
Go oferece suporte a programação de baixo nível quando necessário, incluindo a capacidade de manipular ponteiros, embora a linguagem incentive um estilo de programação seguro. A linguagem Go tem sido amplamente adotada em diversos projetos, desde desenvolvimento de sistemas distribuídos até aplicações de servidores web. Seu design focado na simplicidade, eficiência e concorrência a torna uma escolha popular para muitos desenvolvedores.
2. Configurando ambiente
Para começarmos a programar em Go, precisamos inicialmente configurar o nosso ambiente de desenvolvimento. Veja os passos de como configurar o seu ambiente de acordo com o seu sistema operacional.
Windows 10 ou superior:
Vá até a página https://go.dev/dl para baixar a versão binnária mais recente do instalador. Certifique-se de selecionar a versão compatível com seu sistema operacional, no caso do Windows (.msi)
Faça a instalação, instale na pasta recomendada e finalize.
Para testar a instalação, se ela foi instalada de maneira correta, vá em CMD e digite o comando go version,
para verificar se a instalação foi concluída com sucesso. Uma mensagem de retorno será exibida, algo como
go version go1.22.3 windows/amd64
.
Linux
Debian/Ubuntu:
sudo apt-get update && apt-get upgrade -y && install golang
Arch/Manjaro:
sudo pacman -Syyu && pacman -S go
Fedora:
sudo dnf checkupdate && sudo dnf upgrade -y && sudo dnf install golang
OpenSUSE:
sudo zyper update && sudo zyper upgrade -y && sudo zyper install go
Os comandos citados acima sincronizam e atualizam os repositórios de cada distribuição e instala o Go. Porém, é importante lembrar que nem sempre o repositório da sua distribuição estará com a versão mais atual do Go
Após estar instalado, execute go version
em seu terminal, se estiver tudo tranquilo, instale a extensão do GO
na sua IDE, se tiver algum problema, revise os passos anteriores.
Após instalar o GO na sua IDE, abra o Command Palette (Ctrl+Shift+P) e procure por Go: Install/Update Tools.
Selecione todas as ferramentas e clique em OK. Um terminnal será aberto e começará a atualização das ferramentas,
aguarde até que conclua todas as atualizações. Quando concluir, o terminal mostrará algo como
All tools successfully installed. You are ready to Go. :)
3. Estrutura básica do programa
A estrutura básico de um programa Go é relativamente simples. Veja um exemplo:
// O pacote principal de um programa Go deve ser "main"
package main
// Importação de pacotes necessários.
import "fmt"
// Função principal. A execução do programa começa aqui.
func main() {
// Corpo da função main.
fmt.Println("Hello, World!")
}
Agora, vamos entender um pouco sobre o que está acontecendo na estrutura acima:
Pacote (package):
Cada arquivo Go pertence a um pacote, e o pacote principal de um programa executável deve ser chamado de main. Isso inndica que é um programa executável.
Importação (import):
Após a declaração do pacote, você pode importar outros pacotes necessários para o seu programa. No exemplo, estamos importante o pacote "fmt" para formatação de saída.
Função Principal (func main()):
Todo programa Go executável deve ter uma função chamada main. Esta é a função que será executada quando o programa for iniciado. O código dentro desta função será a entrada principal do seu programa.
Corpo da Função Principal:
O corpo da função main contém o código que será executado quando o programa for iniciado. No exemplo, estamos usando a função fmt.Println para imprimir "Hello, Go!" no console.
Esta é uma estrutura básica, e programas Go podem evoluir para incluir várias funções, pacotes, e estruturas de controle de fluxo. No entanto, todos os programas Go devem ter uma função main no pacote principal para começar a execução.
Agora que você já entende a estrutura básica de um programa, faremos o nosso primeiro programa em Go.
4. Módulos
Em Go, quando iniciamos um novo projeto, sempre começaremos com o comando go mod init. Ele é usado em Go para inicializar um novo módulo (ou converter um projeto existente em um módulo).
A introdução de módulos no Go é uma maneira de gerenciar dependências e versionamento de maneira mais eficiente.
Aqui estão algumas razões pelas quais você deve usar o go mod init:
Gerenciamento de Dependências:
Ao iniciar um módulo com go mod init, você está criando um ambiente onde o Go pode gerenciar suas dependências de forma estruturada. O arquivo go.mod resultante conterá informações sobre os módulos usados pelo seu projeto, incluindo suas versões.
Controle de Versão:
O go.mod inclui informações sobre as versões específicas das dependências utilizadas no projeto. Isso garante que, ао compartilhar o código ou ao reproduzi-lo em outro ambiente, as mesmas versões das dependências serão usadas, promovendo consistência.
Facilita a Colaboração:
O uso de módulos torna mais fácil para outros desenvolvedores colaborarem no seu projeto. Eles podem clonar o repositório e, ao executar go get, o Go baixará automaticamente as dependências corretas.
Compatibilidade com Versionamento Semântico:
O Go incentiva o uso de versionamento semântico (SemVer) para módulos. Isso significa que você pode especificar regras claras sobre como suas dependências podem ser atualizadas, evitando surpresas em atualizações automáticas.
Simplifica o Controle de Versão com Git:
Quando você inicializa um módulo, o go mod init também cria automaticamente uma entrada no arquivo .gitignore para ignorar o diretório vendor/ (onde as dependências são armazenadas). Isso ajuda a manter o controle de versão do seu projeto mais limpo.
Facilita a Migração para o Go Modules:
Para projetos mais antigos, a introdução de módulos pode ser uma tarefa incremental. Usar go mod init é um primeiro passo essencial para migrar um projeto existente para o sistema de módulos.
Iniciando o módulo:
Para iniciar um módulo no seu projeto, certifique-se de estar com o editor de códigos aberto, se você for acostumado com CLI, deixe ele na pasta de seu projeto. No seu editor de códigos, abra a pasta onde iniciaremos o módulo. Após isso, na parte superior esquerda clique em Terminal, depois em New Terminal para abrir o terminal. Abrindo o terminal, verifique se está na pasta correta.
No desenvolvimento real, o caminho do módulo será o local do repositório onde o seu código-fonnto será mantido, por
exemplo: go mod init github.com/meumodulo
Como o objetivo não é passar um projeto real, utilizaremos apenas um exemplo.
Inicie o módulo utilizando o seguinte comandno: go mod init hello/example
Um novo arquivo chamado go.mod será criado na sua pasta.
5. Olá, Mundo!
Finalmente estaremos escrevendo os nossos primeiros códigos em Go, e não poderia ser diferente, né?, começaremos com o famoso "Olá, Mundo!"
Eu particularmente acho besta a lenda que existe no mundo da programação, mas vamos manter apenas para não complicar a explicação.
Primeiramente, crie uma pasta chamada helloworld dentro da pasta onde o arquivo go.mod foi criado. Dentro da pasta helloworld, crie um arquivo chamado hello.go e, dentro do arquivo, digite os comandos necessários para criar o seu primeiro programa.
Segue o exemplo abaixo:
// O pacote principal de um programa Go deve ser "main"
package main
// Importação de pacotes necessários.
import "fmt"
// Função principal. A execução do programa começa aqui.
func main() {
// Corpo da função main.
fmt.Println("Hello, World!")
}
Dentro da pasta Golang temos o nosso arquivo go.mod, a nossa pasta helloworld e, dentro dela, o arquivo hello.go. Ao lado, temos o arquivo hello.go aberto mostrando o código utilizado para criar o nosso primeiro programa que imprime o texto "Olá, Mundo!" na tela.
Como executaremos o programa?
No terminnal, usaremos o comando go run
para compilar e executar programas em Go em um único comando. Ele simplifica
o processo de desenvolvimento, permitindno que você teste e execute rapidamente seu código sem a necessidade de criar
um arquivo executável separado, seguindo nosso exemplo: go run hello.go
.
Uma resposta de "Olá, Mundo!" será exibida na tela.
6. Comentários
Comentários em código servem para fornecer informações adicionais que não são executadas pelo compilador ou interpretador da linguagem, mas são úteis para OS desenvolvedores que leem ou mantêm o código. Aqui estão algumas razões pelas quais os comentários são importantes:
Explicação do Código:
Comentários ajudam a explicar o propósito, a lógica e a funcionalidade do código. Isso torna mais fácil para outros desenvolvedores entenderem o que o código está fazendo.
Documentação do Código:
Comentários podem servir como uma forma de documentação embutida, fornecendo informações sobre como usar funções, classes ou módulos específicos.
Anotações para Desenvolvedores Futuros:
Comentários podem ser úteis para fornecer insights sobre decisões design, possíveis melhorias ou considerações para desenvolvedores futuros que podem precisar modificar ou estender o código. Lembre-se de que a prática de adicionar comentários deve ser equilibrada. Códigos autoexplicativos e bem organizados geralmente precisam de menos comentários. Comentários em excesso ou comentários que não estão sincronizados com o código real podem se tornar obsoletos e prejudicar a manutenção.
Em Go, os comentários são semelhantes a outras linguagens de programação e podem ser usados para fornecer informações adicionais no código fonte. Aqui estão alguns detalhes sobre os comentários em Go:
Comentários de Linha Única:
Comentários de linha única são iniciados com //. Tudo na linha após // é considerado um comentário.
// Isto é um comentário de linha única em Go.
Comentários de Múltiplas Linhas:
Os comentários de múltiplas linhas começam com /* e terminam com */.
/* Este é um comentário de múltiplas linhas em Go.
É util para comentar muitas coisas
de uma só vez!
*/
Também é possível comentar ao lado do seu código, para isso, utilize // ao lado do código que deseja escrever algo e estará fazendo um comentário.
7. Tipos, valores e variáveis
Variáveis em programação são espaços de armazenamento com um nome simbólico (identificador) associado, que contêm dados ou valores manipulados por um programa. Em outras palavras, uma variável é um contêiner que armazena informações e pode ser referenciado ou manipulado em um programa de computador.
Em Go, assim como em muitas outras linguagens de programação, há conceitos fundamentais relacionados a tipos, valores e variáveis. Aqui estão explicações sobre cada um desses conceitos em Go:
Tipos:
Go é uma linguagem de programação estaticamente tipada, o que significa que as variáveis têm tipos definidos em tempo de compilação. Os tipos em Go incluem tipos básicos (inteiros, ponto flutuante, booleanos, etc.) e tipos compostos (structs, slices, maps, arrays, etc.).
Exemplos de tipos básicos:
var idade int // Inteiro
var altura float64 // Ponto flutuante
var ativo bool // Booleano
var nome string // String
Exemplos de tipos compostos:
var numeros [5]int // Array de inteiros com tamanho 5
var dados map[string]int // Mapa de string para int 3
var lista [] float64 // Slice de ponto flutuante
Valores:
Os valores são as instâncias específicas de um tipo. Por exemplo, 42 é um valor do tipo int, e "Olá" é um valor do tipo string.
Os valores em Go têm zero values, o que significa que, se você declara uma variável sem atribuir um valor, ela receberá um valor padrão dependendo do tipo. Veja um exemplo:
var numero int // O valor de "numero" é 0 (zero value para int)
var texto string // 0 valor de "texto" é "" (zero value para string)
Variáveis:
Variáveis em Go são espaços de armazenamento com um nome que armazenam valores de um tipo específico.
Você declara variáveis usando a palavra-chave var, seguida pelo nome da variável e seu tipo, e pode inicializá-las com um valor.
var idade int = 25 // Idade da pessoa (inteiro).
var altura float64 = 175.5 // Altura da pessoa em centímetros (ponto flutuante).
var ativo bool = true // Indica se a pessoa está ativa (booleano).
Em Go, também existe a variável inferida, que refere-se à capacidade do compilador de decidir automaticamente o tipo de uma variável com base no valor que está sendo atribuído a ela. A declaração curta de variáveis “:=” é a principal maneira de criar variáveis inferidas em Go.
Aqui está um exemplo simples:
nome := "João" // Nome da pessoa
idade := 30 // Idade da pessoa
altura := 175.5 // Altura da pessoa
Nesses casos, como mencionado anteriormente, o compilador irá definir automaticamente os tipos, sendo:
- nome é inferido como string.
- idade é inferido como int.
- altura é inferido como float64.
Importante: não é possível criar uma variável usando ":" sem atribuir um valor a ela
Essa abordagem torna a declaração de variáveis mais concisa e ajuda a reduzir a redundância no código. No entanto, é importante observar que a inferência de tipo só é permitida dentro de funções. Fora de funções, você deve usar a forma tradicional de declaração de variáveis com var.
Regra de Nomeação de Variáveis:
Assim como em outras linguagens, Go tem uma regra para a nomeação de variáveis, veja:
- O nome de uma variável pode começar com o caractere (“__”)
- O nome de uma variável não pode começar com número (ex: 1); - O nome de uma variável não pode conter espaços;
- O nome de uma variável não pode ser nenhuma palavra-chave Go (ex: var, println etc.);
- O nome de uma variável só pode conter caracteres alfanuméricos e sublinhados (a-z, A-Z, 0-9 e "_");
- O nome de uma variável é exatamente aquele que você colocou, ou seja, se você utilizar uma letra maiúscula quando declara a variável, deve colocar a mesma letra maiúscula quando chamar a variável (ex: nome, Nome e NOME são variáveis diferentes);
- Não existe um limite para o comprimento do nome da variável;
Existem técnicas que você pode utilizar para criar as suas variáveis, veja algumas:
nome_da_minha_variavel := 12.5 // Estilo Snake Case
nomeDaMinhaVariavel := "Golang" // Estilo Camel Case
NomeDaMinhaVariavel := 150 // Estilo Pascal Case
Cada variável tem um estilo diferente, cada estilo tem o seu nome.
8. Constantes
Em Go, constantes são valores fixos que não variam durante a execução do programa. Elas oferecem uma maneira de nomear e utilizar valores imutáveis que são conhecidos em tempo de compilação. A declaração de constantes é realizada mediante o uso da palavra-chave const, seguida pelo nome da constante e pelo valor que lhe é atribuído. Este mecanismo proporciona clareza consistência ao código, sendo especialmente útil para representar valores que não devem ser alterados ao longo da execução do programa, como constantes matemáticas, limites ou códigos predefinidos.
Aqui estão alguns pontos importantes sobre constantes em Go:
Declaração de Constantes:
Para declarar uma constante, use a palavra-chave const seguida pelo nome da constante e pelo valor.
const pi = 3.1416
const idadeLimite = 18
Tipos em Constantes:
Ao contrário das variáveis, você não precisa explicitamente o tipo da constante, pois o compilador infere o tipo com base no contexto. Mas também é possível declarar o tipo.
const pi float64 = 3.1416
Valores de Expressões Constantes:
Os valores de expressões constantes devem ser determinados em tempo de compilação. Isso significa que você não pode usar funções ou resultados de tempo de execução para inicializar uma constante. Neste exemplo, a função math.Sqrt é chamada para calcular a raiz quadrada de 25. Como esse cálculo só pode ser feito em tempo de execução, não é permitido usar esse valor para inicializar uma constante.
// Válido
const soma = 10 + 20
// Inválido, math.Sqrt é uma função em tempo de execução
const resultado = math.sqrt(25)
Enumerando Constantes:
Você pode criar uma lista de constantes enumeradas usando a palavra-chave iota, que é um identificador que representa valores inteiros incrementados automaticamente.
const (
// iota é uma constante que inicia automaticamente com ◊ e é incrementada
Segunda Feira = iota // 0
TercaFeira // iota incrementa automaticamente, então TercaFeira é 1
QuartaFeira // 2
QuintaFeira // 3
SextaFeira // 4
Sabado // 5
Domingo // 6
)
Constantes Não Tipadas:
As constantes em Go são não tipadas, o que significa que podem ser usadas em diferentes contextos sem a necessidade de conversão explícita.
const x = 42
var y int = x // Atribuição sem conversão explícita
Constantes Exportadas:
Constantes exportadas (iniciadas com letra maiúscula) são visíveis fora do pacote, enquanto constantes não exportadas (iniciadas com letra minúscula) são visíveis apenas dentro do pacote.
const ValorExportado = 100 // Exportada 2 const
valorNaoExportado = 200 // Não exportada
9. Outputs
Em Go, a formatação de saída é comumente realizada utilizando o pacote "fmt". Este pacote fornece funções como Print, Println e Printf para exibir informações no console ou em outros destinos de saída. Além disso, o pacote "fmt" também oferece verbos de formatação, que são especificadores utilizados para formatar valores de acordo com seus tipos.
fmt.Print() - Impressão Simples:
A função Print é utilizada para imprimir valores no console sem adicionar uma nova linha ao final. Ela aceita uma lista variável de argumentos e os imprime consecutivamente. Exemplo:
nome = "Alice"
idade := 30
fmt.Print("Nome: ", nome, ", Idade: ", idade)
// Saída | Nome: Alice, Idade: 30
fmt.Println() - Impressão com Nova Linha:
A função Println é semelhante à Print, mas adiciona automaticamente uma nova linha ao final da saída. Isso é útil para separar as linhas de saída. Exemplo:
nome := "Bob"
idade := 25
fmt.Println("Nome:", nome, "Idade:", idade) // Saída | Nome: Bob Idade: 25
fmt.Printf() - Impressão com Formatação:
A função Printf permite formatação de string usando verbos específicos, sendo semelhante à função Printf em C. Ela aceita uma string de formato e uma lista variável de argumentos que serão formatados e impressos. Exemplo:
nome = "Charlie"
idade := 35
fmt.Printf("Nome: %s, Idade: %d\n", nome, idade) // Saída | Nome: Charlie, Idade: 35
Essas funções do pacote "fmt" são fundamentais para exibir informações no console de maneira fácil e eficiente. A escolha entre Print, Println, ou Printf depende das necessidades específicas de formatação e da presença ou ausência de nova linha ao final da saída.
10. Verbos de formatação
Em Go, os verbos de formatação são especificadores que indicam como os valores devem ser formatados ao serem exibidos usando a função Printf do pacote "fmt". Aqui estão alguns dos verbos de formatação mais comuns em Go:
%v (Verbo de Valor):
Utilizado para imprimir o valor de uma variável na sua forma natural, sem formatação específica.
nome = "Alice"
idade := 25
fmt.Printf("Nome: %v, Idade: %v\n", nome, idade) // Saída: Nome: Alice, Idade: 25
%d (Verbo Decimal):
Utilizado para formatar números inteiros.
numero := 42
fmt.Printf("Número: %d\n", numero)
// Saída: Número: 42
%f (Verbo Ponto Flutuante):
Utilizado para formatar números de ponto flutuante.
altura := 175.5
fmt.Printf("Altura: %f\n", altura) // Saída: Altura: 175.500000
%s (Verbo String):
Utilizado para formatar strings.
mensagem := "Olá, mundo!"
fmt.Printf("Mensagem: %s\n", mensagem) // Saída: Mensagem: Olá, mundo!
%t (Verbo Booleano):
Utilizado para formatar valores booleanos.
ativo := true
fmt.Printf("Ativo: %t\n", ativo) // Saída: Ativo: true
%b (Verbo Binário):
Utilizado para formatar números em representação binária.
numero := 10
fmt.Printf("Número em binário: %b\n", numero) // Saída: Número em binário: 1010
Esses são apenas alguns dos verbos de formatação disponíveis em Go, existem diversos. A escolha do verbo depende do tipo de valor que você está formatando e do resultado desejado na saída formatada.
11. Tipos de dados
Os tipos de dados em Go são essenciais para definir como os valores são armazenados na memória, interpretados e manipulados pelo programa. Cada variável em Go possui um tipo de dados associado, que determina as operações que podem ser realizadas com ela, a quantidade de memória que ela ocupa e a forma como ela é representada internamente.
Go é uma linguagem estaticamente tipada, ou seja, uma vez que for definido um tipo numa variável, ela só poderá armazenar dados desse tipo.
Esses são os três tipos básicos de dados em Go:
- bool: representa um valor booleano, que será falso ou verdadeiro;
- numérico: representa tipos inteiros, valores de ponto flutuante e tipos complexos;
- string: representa uma sequência de caracteres.
Aqui está um exemplo dos tipos de dados básicos em Go:
var verdadeiro bool = true // Valor booleano
var numero int = 5 // Inteiro
var flutuante float32 = 3.14 // Ponto flutuante
var caracteres string = "Hi!" // Conjunto de caracteres
Boolean:
Em Go, o tipo de dado bool representa valores booleanos, que podem ter apenas dois estados: verdadeiro (true) ou falso (false). O tipo bool é frequentemente utilizado em expressões condicionais, estruturas de controle de fluxo e operações lógicas.
Aqui estão alguns pontos importantes sobre o tipo bool em Go:
Declaração de Variáveis:
Você pode declarar variáveis do tipo bool da seguinte maneira:
var isTrue bool // retorna true (verdadeiro)
var isFalse bool = false // retorna false (falso)
Ou, usando inferência de tipo:
isTrue := true // retorna true (verdadeiro)
isFalse := false // retorna false (falso)
O tipo bool é simples, mas é uma peça fundamental na programação, sendo essencial para tomada de decisões e controle de fluxo em algoritmos e lógica condicional.
Integer:
Em Go, os tipos de dados inteiros são usados para representar números inteiros. Go fornece vários tipos de inteiros, com diferentes tamanhos e faixas, para atender necessidades de programação.
O tipo de dados inteiro possui duas categorias:
- Inteiros assinados: armazenam valores positivos e negativos;
- Inteiros não-assinados: armazenam apenas não negativos.
Importante: o tipo padrão para inteiro é int. Se você não especificar um tipo, o tipo será int.
Signed Integers (Inteiros Assinados):
Em Go, os inteiros assinados são representados pelos tipos int e outros tipos específicos, como int8, int16, int32 e int64. Esses tipos são usados para armazenar números inteiros que podem ser positivos, negativos ou zero, e têm a capacidade de representar o sinal (positivo ou negativo) do número.
Aqui estão alguns detalhes sobre inteiros assinados em Go:
Tipo (int):
O tipo int representa inteiros com sinal. O tamanho do int é dependente da arquitetura do sistema onde o código está sendo compilado, sendo um int32 em sistemas de 32 bits e um int64 em sistemas de 64 bits.
Exemplo:
var numeroInteiro int = 667 // Armazena o valor 667
numeroInteiroNegativo := -999 // Armazena o valor -999
Existem cinco palavas-chave/tipos de inteiros assinados em Go, cada um com o seu tamanho e faixa:
Tipo (int):
- Tamanho: depende da plataforma, sendo 32 bits em sistemas de 32 bits e 64 bits em sistemas de 64 bits.
- Faixa: -2147483648 a 2147483647 em sistemas de 32 bits e- 9223372036854775808 a 9223372036854775807 em sistemas de 64 bits.
Tipo (int8):
- Tamanho: 8 bits/1 byte.
- Faixa: -128 a 127.
Tipo (int16):
- Tamanho: 16 bits/2 bytes.
- Faixa: -32768 a 32767.
Tipo (int32):
- Tamanho: 32 bits/4 bytes.
- Faixa: -2147483648 a 2147483647.
Tipo (int64):
- Tamanho: 64 bits/8 bytes.
- Faixa: -9223372036854775808 a 9223372036854775807.
Esses são os tipos de dados inteiros em Go, cada um com seu tamanho e faixa específicos. O tipo int é dependente da plataforma, o que significa que em sistemas de 32 bits, ele terá 32 bits, e em sistemas de 64 bits, terá 64 bits. Os tipos fixos, como int8, int16, int32 e int64, têm tamanhos fixos independentemente da plataforma. Cada tipo tem um intervalo de valores que pode representar, sendo importante considerar esses limites ao escolher o tipo de dados apropriado para a sua aplicação.
Signed Integers (Inteiros não-assinados):
Em Go, os inteiros não assinados são representados pelos tipos uint e outros tipos específicos, como uint8, uint16, uint32 e uint64. Diferentemente dos inteiros assinados, os inteiros não assinados representam apenas valores não negativos (zero e positivos) e não têm um bit de sinal. Aqui estão alguns detalhes sobre inteiros não assinados em Go:
uint: O tipo uint representa inteiros sem sinal. O tamanho do uint é dependente da arquitetura do sistema onde o código está sendo compilado, sendo um uint32 em sistemas de 32 bits e um uint64 em sistemas de 64 bits. Exemplo:
var numeroInteiro uint = 4545 // Armazena o valor 4545
var numeroNegativo uint = -11 // Erro: o tipo "uint" não recebe valores negativos
Existem cinco palavas-chave/tipos de inteiros não-assinados em Go, cada um com o seu tamanho e faixa:
Tipo(uint):
- Tamanho: depende da plataforma, sendo 32 bits em sistemas de 32 bits e 64 bits em sistemas de 64 bits.
- Faixa: 0 a 4294967295 em sistemas de 32 bits e 0 a 18446744073709551615 em sistemas de 64 bits.
Tipo(uint8):
- Tamanho: 8 bits/1 byte.
- Faixa: 0 a 255.
Tipo(uint16):
- Tamanho: 16 bits/2 bytes.
- Faixa: 0 a 65535.
Tipo(uint32):
- Tamanho: 32 bits/4 bytes.
- Faixa: 0 a 4294967295.
Tipo(uint64):
- Tamanho: 64 bits/8 bytes.
- Faixa: 0 a 18446744073709551615.
Esses são os tipos de dados inteiros não assinados em Go, cada um com seu tamanho e faixa específicos. Os tipos uint e suas variantes representam valores que são sempre não negativos, incluindo zero e valores positivos, e são frequentemente usados quando você precisa representar quantidades que não fazem sentido como valores negativos.
Float:
O tipo de dados float em programação, incluindo em Go, é usado para armazenar valores numéricos que representam números de ponto flutuante, ou seja, números reais. Esses números podem ter parte inteira e parte fracionária, permitindo a representação de valores não inteiros, como números decimais.
O tipo de dados float possui duas palavras-chave:
Tipo(float32):
- Tamanho: 32 bits (4 bytes).
- Precisão: aproximadamente 7 dígitos decimais de precisão.
- Faixa: aproximadamente de -3.4e+38 a 3.4e+38.
- Significado: O tipo float32 em Go representa números de ponto flutuante (números reais) com 32 bits de precisão. Isso significa que pode representar números decimais com até sete dígitos significativos, e sua faixa de valores vai de aproximadamente -3.4 vezes 10 elevado a 38 até 3.4 vezes 10 elevado a 38.
Exemplo:
var flutuante float32 = 11.11 // Recebe um valor flutuante
var Flutuante float32 = -3.2e+35 // Recebe um valor flutuante negativo
Tipo(float64):
- Tamanho: 64 bits (8 bytes).
- Precisão: aproximadamente 15 dígitos decimais de precisão. - Faixa: aproximadamente de -1.7e+308 a +1.7e+308.
- Significado: O tipo float64 em Go representa números de ponto flutuante com maior precisão, usando 64 bits. Isso permite representar números decimais com até 15 dígitos significativos. A faixa de valores do float64 vai de aproximadamente -1.7 vezes 10 elevado a 308 até 1.7 vezes 10 elevado a 308.
Exemplo:
var flutuante float64 = -1.3e+220 // Recebe um valor flutuante negativo
var Flutuante float64 = 111.111 // Recebe um valor flutuante
Em resumo, a principal diferença entre float32 e float64 está na precisão e na faixa de valores que podem ser representados. O float64 oferece maior precisão, mas também ocupa mais espaço em memória, sendo a escolha preferida quando a precisão é crucial, como em cálculos científicos. O float32 é usado quando se deseja economizar espaço e a precisão adicional não é necessária.
String:
O tipo de dados string é empregado para armazenar sequências de caracteres, ou seja, texto. Os valores atribuídos a uma string devem ser delimitados por aspas duplas. Essa representação é fundamental para trabalhar com dados textuais em Go, proporcionando uma maneira consistente e eficiente de lidar com informações como palavras, frases e outras formas de texto.
Aqui estão algumas características importantes sobre strings em Go:
Declaração de Strings:
Você pode declarar uma string usando aspas duplas.
mensagem := "Gopher Golang ^-^/!!!" // Essa é uma String
Imutabilidade: Strings em Go são imutáveis, o que significa que, uma vez criada, não é possível modificar seus caracteres individualmente. As operações que parecem modificar uma string, na verdade, criam uma nova string.
As strings em Go são projetadas para serem eficientes, e a codificação em UTF-8 permite a manipulação eficaz de caracteres em diferentes idiomas. A imutabilidade das strings promove a segurança na manipulação de dados e evita comportamentos inesperados. O pacote strings oferece funcionalidades adicionais para facilitar o trabalho com strings de maneira eficiente.
12. Arrays
Em Go, um array é um tipo de estrutura de dados que permite armazenar uma coleção fixa de elementos do mesmo tipo, ou seja, uma variável que armazena valores do mesmo tipo. Ao serem declarados, os arrays têm um tamanho predefinido que não pode ser alterado após sua criação. Alguns pontos essenciais sobre arrays em Go incluem:
Declaração de Arrays:
Arrays são declarados especificando o tipo dos elementos e seu tamanho. A sintaxe básica é utilizando "var" é:
var nomeArray = [tamanho]Tipo{valores} // Aqui o comprimento é definido
var nomeArray = [...]Tipo{valores} // Aqui o comprimento é inferido
Com o sinal “:=” é assim:
nomeArray = [tamanho] Tipo{valores} // Aqui o comprimento é definido
nomeArray = [...]Tipo{valores} // Aqui o comprimento é inferido
Importante: A dimensão especifica a quantidade de elementos a serem armazenados no array. Em Go, os arrays possuem uma dimensão fixa. Essa dimensão é definida explicitamente por um número ou inferida pelo compilador (o que significa que o compilador decide a dimensão do array com base no número de valores fornecidos).
Acesso a Elementos de um Array:
Você pode acessar um elemento específico do array consultando seu número de índice.
Em Go, os índices do array começam em 0. Isso significa que [0] é o primeiro elemento, [1] é o segundo elemento, etc.
Exemplo:
contando := [5]int{10, 20, 30, 40, 50}
fmt.Println(contando [0]) // Imprime o valor 10
fmt.Println(contando [4]) // Imprime o valor 50
Alterar Elementos de um Array:
Você também pode alterar o valor de um elemento específico da matriz consultando o número do índice.
Exemplo:
contando := [5]int{10, 20, 30, 40, 50}
contando [4] = 10 // Alterando o valor do quarto elemento no array "contando"
fmt.Println(contando) // Imprimindo o valor do array contando após a alteração
Inicialização de Arrays:
Se um array ou qualquer um de seus elementos não for explicitamente inicializado no código, será atribuído a eles o valor padrão correspondente ao seu tipo. É relevante notar que, para o tipo “int", o valor padrão é 0, enquanto para o tipo "string", o valor padrão é uma string vazia, representada por """. Essa característica garante consistência e previsibilidade quando não há uma inicialização explícita, proporcionando valores padrão coerentes com os tipos de dados envolvidos.
Exemplo:
array1 := [7]int{1,2,3,4,5,6,7} // Completamente inicializado
array2 := [7]int{1,2,3,4} // Parcialmente inicializado
array3 := [7]int{} // Não inicializado:
fmt.Println(array1) // Imprime todos os valores
fmt.Println(array2) // Imprime parte dos valores
fmt.Println(array3) // Imprime o valor padrão (0)
Inicialização de Elementos Específicos em Arrays:
É possível inicializar apenas elementos específicos de um array. Exemplo:
array := [7]int{0:10, 6:70} // Inicializando elementos específicos
fmt.Println(array) // Imprimindo os elementos específicos
Explicando o exemplo acima:
O array possui 7 elementos ([7]), ao utilizar “0:10", estou atribuindo o valor "10" ao índice 0 do array (primeiro elemento). O “6:70" indica que estou atribuindo 70 ao índice 6 (sétimo elemento).
Comprimento de Arrays:
O tamanho de um array em Go é fixo e determinado durante a declaração. Para obter o tamanho, utiliza-se a função "len".
Exemplo:
array1 = [3]string{"Maçã", "Uva", "Jambo"}
array2 = [...]int{1,2,3,4,5,6,7}
fmt.Println(len(array1)) // Imprime o comprimento do primeiro Array (3)
fmt.Println(len(array2)) // Imprime o comprimento do segundo Array (7)
Os arrays em Go são uma ferramenta fundamental quando se precisa de uma coleção fixa de elementos do mesmo tipo, proporcionando um controle rígido sobre o tamanho da estrutura de dados. Entretanto, para situações mais dinâmicas, as slices são geralmente mais convenientes e amplamente utilizadas.
13. Slices
As slices são estruturas semelhantes aos arrays, mas oferecem maior poder e flexibilidade. Assim como os arrays, as slices são utilizadas para armazenar vários valores do mesmo tipo em uma única variável. No entanto, ao contrário dos arrays, as slices têm a capacidade de alterar dinamicamente seu comprimento, expandindo ou reduzindo conforme necessário. Essa característica confere às slices uma adaptabilidade que as torna especialmente úteis em situações onde a quantidade de dados pode variar ao longo do tempo.
Em Go, existem várias formas de criar uma slice, veja:
- Usando o formato: []Tipo{valores};
- Criando uma slice de um array;
- Usando a função make().
Criando uma Slice com o formato: []Tipo{valores}:
// Sintaxe
minhaSlice := []Tipo{valores}
// Forma comum de declarar uma slice
minhaSlice := []int{}
// 0 código acima declara uma slice vazia de comprimento e capacidade
// Para inicializar a slice durante a declaração, é feita dessa forma:
minhaSlice := []int{1,2,3}
// 0 código acima declara uma slice de inteiros de comprimento 3 e capacidade de 3
Em Go, existem duas funções que podem ser usadas para retornar o comprimento e a capacidade de uma fatia:
- Função len() - retorna o comprimento da slice (o número de elementos na slice)
- Função cap() - retorna a capacidade da slice (o número de elementos para os quais a slice pode aumentar ou diminuir)
len - Comprimento de uma Slice:
A função "len" retorna o número de elementos presentes em uma slice. Por exemplo:
minhaSlice := []int{1, 2, 3, 4, 5}
comprimento := len(minhaSlice) // Retorna 5
fmt.Println(comprimento) // Imprime o valor da variável comprimento
cap - Capacidade de uma Slice:
A função cap retorna a capacidade total da slice, ou seja, o número máximo de elementos que a slice pode conter antes de precisar ser realocada para um espaço maior na memória.
minhaSlice := make([]int, 3, 5)
capacidade := cap (minhaSlice) // Retorna 5
fmt.Println(capacidade) // Imprime o valor da variável capacidade
A capacidade é especialmente útil quando se trabalha com slices que podem crescer dinamicamente, pois permite otimizar a realocação de memória quando necessário.
Ambas as funções são ferramentas valiosas ao manipular slices em Go, proporcionando informações essenciais sobre a quantidade atual de elementos e a capacidade disponível na estrutura de dados.
Criando uma Slice de um Array:
Você pode criar uma slice fatiando um array. Exemplo:
// Criando uma slice de um array existente
var minhaArray = [5]int{1, 2, 3, 4, 5} // Array
minhaSlice := minhaArray[1:4] // Cria uma slice contendo os elementos 2, 3, 4
fmt.Println(minhaSlice) // Imprime o valor "[2 3 4]" na tela
Veja outro exemplo:
minhaArray := [7]int{20, 21, 22, 23, 24, 25, 26}
minhaSlice := minhaArray[2:4]
fmt.Printf("minhaSlice = %v\n", minhaSlice)
fmt.Printf("comprimento = %d\n", len(minhaSlice))
fmt.Printf("capacidade = %d\n", cap(minhaSlice))
No exemplo acima, minhaSlice é uma fatia com comprimento de 2. Ela é feita a partir de minhaArray, que é um array com comprimento 7. A Slice começa no terceiro elemento do array que tem o valor 22 (lembre-se que os índices do array começam em 0. Isso significa que "O" é o primeiro elemento, 1 é o segundo e etc.). A slice pode crescer até o final da array, isso significa que a capacidade da slice é 5.
Criando uma Slice coma função make():
A função make() também pode ser utilizada para criar uma slice.
// Sintaxe
nome_da_slice := make([]Tipo, comprimento, capacidade)
Importante: se o parâmetro capacidade não for definido, será igual ao comprimento.
Este exemplo mostra como criar uma slice com a função make():
// Criando um slice de inteiros com make
// make([] tipo, tamanho)
minhaSlice := make([]int, 3) // make([]tipo, tamanho)
// Modificando valores no slice
minhaSlice[0] = 10
minhaSlice [1] = 20
minhaSlice [2] = 30
fmt.Println("Slice :", minhaSlice)
fmt.Println("Comprimento:", len(minhaSlice))
fmt.Println("Capacidade:", cap(minhaSlice))
Neste exemplo, make([]int, 3) cria uma slice de inteiros com um comprimento inicial de 3. Em seguida, valores são atribuídos aos elementos do slice.
Acessando Elementos de uma Slice:
Em Go, você pode acessar os elementos de uma slice usando índices. O índice do primeiro elemento é 0, o segundo é 1, e assim por diante. Aqui está um exemplo simples de como acessar elementos de uma slice:
minhaSlice := []int{10,20,30,40,50}
fmt.Println(minhaSlice[0]) // Imprime o primeiro elemento (10)
fmt.Println(minhaSlice [4]) // Imprime o último elemento (50)
Neste exemplo, minhaSlice é uma slice de inteiros, com cinco elementos. Os elementos são acessados usando "[]", seguidos do índice desejado (ex: índice [0] retorna o valor 10).
Alterando Elementos de uma Slice:
Você pode alterar os elementos de uma slice atribuindo um novo valor a um elemento específico usando o índice correspondente. Aqui está um exemplo mais detalhado de como fazer isso:
minhaSlice := []int{10, 20, 30}
minhaSlice [2] = 8000 // Alterando o valor do último elemento (30)
fmt.Println(minhaSlice[0]) // Imprime o primeiro elemento (10)
fmt.Println(minhaSlice[2]) // Imprime o último elemento (8000)
Neste exemplo, minhaSlice é uma slice de inteiros inicializada com três elementos. O elemento de índice [2] (terceiro elemento) sofreu uma modificação, saindo do valor 30 para 8000.
Anexando Elementos numa Slice:
Você pode anexar elementos ao final de uma slice utilizando a função "append()":
// Sintaxe
nome_da_slice = append (nome_da_slice, elementol, elemento2, ...)
Veja um exemplo:
minhaSlice := []int{10, 20, 30} // Criando uma slice de inteiros
fmt.Println("Slice antes da adição:", minhaSlice) // Exibindo a slice antes da adição
minhaSlice = append(minhaSlice, 40) // Adicionando um novo elemento (40) à slice
minhaSlice = append(minhaSlice, 50, 60, 70) // Adicionando vários elementos à slice
fmt.Println("Slice após a adição:", minhaSlice) // Exibindo a slice após a adição
Neste exemplo, minhaSlice é uma slice de inteiros inicializada com três elementos. A função append() é usada para adicionar novos elementos à slice.
Anexando uma Slice numa Slice:
Para anexar todos os elementos de uma slice a outra slice, use a função append().
// Sintaxe
minhaSlice = append(minhaSlicel, minhaSlice2...)
Importante: O após minhaSlice2 é necessário ao anexar os elementos de uma slice a outra. Aqui está um exemplo de como anexar uma slice a outra:
minhaSlice := []int{1,2,3} // Primeira slice
minhaSlice2 := []int{4,5,6} // Segunda Slice
minhaSlice3 := append(minhaSlice, minhaSlice2...) // Terceira slice
fmt.Printf("minhaSlice3 = %v\n", minhaSlice3) // Resultado da terceira slice
fmt.Printf("Comprimento= %d\n", len(minhaSlice3)) // Resultado do comprimento
fmt.Printf("Capacidade %d\n", cap(minhaSlice3)) // Resultado da capacidade
No exemplo acima, minhaSlice3 utiliza a função append() para anexar minhaSlice e minhaSlice2. O resultado disso será a junção dos elementos de minhaSlice e minhaSlice2.
Eficiência de Memória:
Ao utilizar slices em Go, todos os elementos do array subjacente são carregados na memória. Caso o array seja extenso e você necessite apenas de alguns elementos, torna-se mais eficiente copiar exclusivamente esses elementos utilizando a função copy().
A função copy() cria um novo array subjacente contendo apenas os elementos essenciais para a slice de destino. Essa abordagem contribui para a redução do consumo de memória do programa, evitando a carga de dados não utilizados.
Portanto, ao empregar copy(), otimiza-se a alocação de memória, assegurando que somente os dados relevantes sejam copiados para a nova slice. Essa prática é particularmente valiosa ao lidar com grandes conjuntos de dados, visando minimizar o impacto nos recursos do sistema.
Este exemplo mostra como usar a função copy():
// Origem
source := []int{1, 2, 3, 4, 5}
// Destino com capacidade inicial
destination := make([]int, 3)
// Usando copy para copiar elementos da origem para o destino
// 0 tamanho da cópia é o menor entre len(source) e len(destination) 9 := copied copy (destination, source)
fmt.Println("Elementos copiados:", copied)
fmt.Println("Destino após a cópia:", destination)
Neste exemplo, a função copy(destination, source) copia elementos da slice source para a slice destination. A capacidade inicial da destination é suficiente para armazenar três elementos, então apenas os três primeiros elementos de source serão copiados.
14. Operadores
Operadores são utilizados para executar operações em variáveis e valores. Por exemplo, o operador "+" é usado para somar dois valores. Veja o exemplo abaixo:
var soma = 1900 + 45 // Somando dois valores
fmt.Println(soma) // Imprimindo valor da variável "soma"
O operador "+" é comumente utilizado para adicionar dois valores, mas também pode ser empregado para somar uma variável a um valor ou para somar duas variáveis. Exemplo:
var (
soma = 1900 + 14 // Recebe o valor de 1900 + 14 (1914)
soma2 = 1900 + 29 // Recebe o valor de 1900 + 29 (1929)
soma3 = 1900 + 45 // Recebe o valor de 1900 + 45 (1945)
soma4 = soma4 = soma + soma2 + soma3 // Recebe os valores de soma, soma2 e soma3
)
fmt.Println(soma4) // Imprimindo valor da variável "soma4"
Go divide os operadores nos seguintes grupos:
- Aritméticos;
- Atribuição;
- Comparação;
- Lógicos;
- Bit a Bit (Bitwise).
Operadores aritméticos:
Operadores aritméticos são usados para realizar operações matemáticas comuns. Veja a tabela com os operadores, nomes, descrições e exemplos:
OPERADOR | NOME | DESCRICÃO | EXEMPLO |
---|---|---|---|
+ | Soma | Adiciona dois valores | x + y |
- | Subtração | Subtrai dois valores | x - y |
* | Multiplicação | Multiplica dois valores | x * y |
/ | Divisão | Divide dois valores | x / y |
% | Modulo | Retorna o resto da divisão entre dois valores. | x % y |
** | Potência | Eleva um valor a outro | x ** y |
++ | Incremento | Incrementa um valor | x++ |
-- | Decremento | Decrementa um valor | x-- |
Esta lista mostra exemplos e equivalências dos operadores aritméticos em Go, que simplificam a expressão de operações frequentes.
Operadores de atribuição:
Operadores de atribuição são usados para atribuir valores a variáveis. No exemplo abaixo, o operador de atribuição (=) é usado para atribuir o valor 15 a uma variável chamada valor:
var soma = 15 // Variável "soma" recebe o valor "15"
2 fmt.Println(soma) // Imprimindo o valor da variável soma na tela
O operador de atribuição de adição (+=) adiciona um valor a uma variável:
var soma = 15 // Variável "soma" recebe o valor "15"
soma += 5 // Adicionando "+5" à variável soma
fmt.Println(soma) // Imprimindo o valor da variável soma na tela
Uma lista de todos os operadores de atribuição:
OPERADOR | NOME | EXEMPLO |
---|---|---|
= | Atribuição | x = 5 |
+= | Atribuição de adição | x += 3 |
-= | Atribuição de subtração | x -= 3 |
*= | Atribuição de multiplicação | x *= 3 |
/= | Atribuição de divisão | x /= 3 |
%= | Atribuição de modulo | x %= 3 |
&= | Atribuição de bitwise e | x &= 3 |
^= | Atribuição de bitwise xor | x ^= 3 |
>>= | Atribuição de bitwise shift direito | x >>= 3 |
Esta lista mostra exemplos e equivalências dos operadores de atribuição compostos em Go, que simplificam a expressão de operações frequentes.
Operadores de comparação:
Operadores de comparação são usados para comparar dois valores.
Importante: o valor de retorno de uma comparação é verdadeiro (1) ou falso (0).
No exemplo a seguir, o operador maior que (>) é usado para descobrir se 10 é maior que 5:
var valor1 = 10
var valor2 = 5
fmt.Println(valor1 › valor2) // Retorna (verdadeiro) porque 10 é maior que 5
Uma lista de todos os operadores de comparação:
/*
Operador: >
Nome: Maior que
Descrição: Retorna verdadeiro se o primeiro valor for maior que o segundo
Exemplo: x > y
Operador: <
Nome: Menor que
Descrição: Retorna verdadeiro se o primeiro valor for menor que o segundo
Exemplo: x < y
Operador: ==
Nome: Igual a
Descrição: Retorna verdadeiro se os valores forem iguais
Exemplo: x == y
Operador: !=
Nome: Diferente de
Descrição: Retorna verdadeiro se os valores forem diferentes
Exemplo: x != y
Operador: >=
Nome: Maior ou igual a
Descrição: Retorna verdadeiro se o primeiro valor for maior ou igual ao segundo
Exemplo: x >= y
Operador: <=
Nome: Menor ou igual a
Descrição: Retorna verdadeiro se o primeiro valor for menor ou igual ao segundo
Exemplo: x <= y
Essa tabela resume os operadores de comparação, descrevendo seus nomes, exemplos de uso e a tradução correspondente.
Importante: você aprenderá mais sobre operadores de comparação e como usá-los no capítulo Condições.
Operadores bit a bit:
Operadores bit a bit são usados em números (binários).
Essa é a lista de todos os operadores bit a bit:
/*
Operador: &
Nome: AND
Descrição: Define cada bit como 1 se ambos os bits forem 1
Exemplo: x & y
Operador: │
Nome: OR
Descrição: Define cada bit como 1 se um dos dois bits for 1
Exemplo: x | y
Operador: ^
Nome: XOR
Descrição: Define cada bit como 1 se apenas um dos dois bits for 1
Exemplo: x ^ y
Operador: <<
Nome: Deslocamento à esquerda com preenchimento zero
Descrição: Desloca cada bit para a esquerda, inserindo zeros à direita
Exemplo: x << 2
Operador: >>
Nome: Deslocamento à direita com preenchimento zero
Descrição: Desloca cada bit para a direita, inserindo zeros à esquerda
Exemplo: x >> 2
15. Estruturas condicionais
Em Go, as estruturas condicionais, como "if", "else if', e "else", proporcionam o controle do fluxo de execução do programa conforme determinadas condições. Essas construções permitem que você tome decisões lógicas, adaptando o comportamento do programa de acordo com os diferentes cenários que podem surgir durante a execução.
Uma condição pode ser verdadeira ou falsa.
Go suporta os operadores de comparação usuais da matemática:
- Menos que: <
- Menor ou igual: <=
- Maior que: >
- Maior ou igual: >=
- Igual a: ==
- Diferente de: !=
Além disso, Go oferece suporte aos operadores lógicos usuais:
- Operador lógico "and": &&
- Operador lógico "or": ||
- Operador lógico "not": !
Você pode usar esses operadores ou suas combinações para criar condições para diferentes decisões.
Declarando if:
A declaração if é usada para executar um bloco de código condicionalmente, dependendo da avaliação de uma expressão booleana. Veja a sintaxe básica:
if condição {
// Bloco de código a ser executado se a condição for verdadeira
}
Aqui está um exemplo básico de como declarar if em Go:
// Variável com tipo inferido recebendo o valor 18
idade := 18
// Verificando se "idade" é maior que 17
if idade > 17 {
fmt.Println("Você é maior de idade.")
}
No exemplo acima, temos dois valores, sendo que, um deles, está armazenado numa variável (idade). Testamos os dois valores para descobrir se o valor armazenado na variável é maior que 17. Se a condição for verdadeira, um texto é imprimido.
Declarando else:
A declaração else em Go é usada em conjunto com a declaração if para especificar um bloco de código a ser executado quando a condição do if for falsa. Veja a sintaxe:
if condição {
// Código a ser executado se a condição for verdadeira } else {
} else {
// Código a ser executado se a condição for falsa
}
Aqui está um exemplo simples:
// Variável com tipo inferido recebendo o valor 15
idade := 15
// Verificando se "idade" é maior que 18
if idade > 18 {
fmt.Println("Você é maior de idade.")
} else {
fmt.Println("Você é menor de idade.")
}
No exemplo acima, se a condição idade > 18 for verdadeira, o bloco de código dentro do if será executado. Caso contrário, o bloco de código dentro do else será executado.
Importante: ao declarar eles, certifique-se de estar na linha certa, caso contrário, um erro de sintaxe será gerado. Veja um exemplo:
if idade > 18 {
fmt.Println("Você é maior de idade.")
} // 0 else deve estar aqui, logo após o fechamento de chaves do if else { // 0 else abaixo da chave do if causará erro
else {
fmt.Println("Você é menor de idade.")
}
Declarando else if:
A declaração else if em Go é utilizada para avaliar múltiplas condições em sequência, permitindo que você lide com diferentes cenários de maneira estruturada. Dê uma olhada na sintaxe:
if condição1 {
// Código a ser executado se condição1 for verdadeira
} else if condição2 {
// Código a ser executado se condição1 for falsa e condição2 for verdadeira }
else {
// Código a ser executado se tanto condição1 quanto condição2 forem falsas
}
Aqui está um exemplo básico:
nota := 75
if nota >= 90 {
fmt.Println("Excelente!")
} else if nota >= 70 {
fmt.Println("Bom trabalho!")
} else {
fmt.Println("É necessário melhorar.")
}
No exemplo acima, três blocos de código são associados a diferentes condições:
Se nota for maior ou igual a 90, o primeiro bloco dentro do if é executado, imprimindo "Excelente!".
Se a primeira condição não for atendida e nota for maior ou igual a 70, o segundo bloco dentro do else if é executado, imprimindo "Bom trabalho!".
Se nenhuma das condições anteriores for verdadeira, o bloco dentro do else é executado, imprimindo "É necessário melhorar.".
Essa estrutura permite que você lide com uma série de condições de forma organizada e fornece flexibilidade para expressar a lógica condicional do seu programa.
Declarando if aninhado:
É possível utilizar estruturas if aninhadas para lidar com condições mais complexas ou para realizar verificações adicionais em um bloco condicional. Veja a sintaxe:
if condição1 {
// Código a ser executado se condiçãol for verdadeira
if condição2 {
// Código a ser executado se tanto condição1 quanto condição2 forem verdadeiras
}
}
Aqui está um exemplo de como declarar if aninhados:
idade := 25
nome = "Alice"
if idade >= 18 {
fmt.Println("Você é maior de idade.")
if nome == "Alice" {
fmt.Println("Bem-vinda, Alice!")
} else {
fmt.Println("Olá, pessoa maior de idade!")
} else {
fmt.Println("Você é menor de idade.")
}
}
Neste exemplo, há um if aninhado dentro do bloco de código do primeiro if. O segundo if verifica se o nome é "Alice" dentro do contexto de uma pessoa maior de idade. Dependendo das condições, diferentes mensagens serão impressas.
Ao utilizar if aninhados, é essencial manter uma boa organização no código para facilitar a leitura e compreensão. Essa técnica é útil quando você precisa verificar várias condições em diferentes níveis de hierarquia.
16. Switch
Em Go, a declaração switch é uma maneira flexível e poderosa de expressar estruturas condicionais em que você compara uma expressão com vários valores possíveis.
A instrução switch em Go é semelhante à de outras linguagens como C, C++, Java, JavaScript e PHP, mas com uma diferença fundamental: em Go, não é necessário usar a instrução break após a execução de uma case. A sintaxe básica é a seguinte:
switch expressao {
case valor1:
// Código a ser executado se a expressão for igual a valor1
case valor2:
// código a ser executado se a expressão for igual a valor2
// ... outros cases
default:
// código a ser executado se nenhum dos casos anteriores for correspondido
É assim que funciona:
- A expressão é avaliada uma vez; O valor da expressão do switch é comparado com os valores de cada case;
- Se houver uma correspondência, o bloco de código associado é executado;
- A palavra-chave default é opcional. Ela especifica algum código a ser executado se não houver correspondência de case.
Aqui está um exemplo para ilustrar como o switch funciona:
diaDaSemana := "quarta-feira"
switch diaDaSemana {
case "segunda-feira":
fmt.Println("Primeiro dia útil da semana.")
case "quarta-feira":
fmt.Println("Dia da metade da semana. ")
case "sexta-feira":
fmt.Println("Chegou o fim de semana!")
default:
fmt.Println("Outro dia da semana.")
}
Neste exemplo, a expressão diaDaSemana é comparada com diferentes valores em cada case. O código dentro do bloco correspondente ao primeiro caso cuja expressão é igual ao valor de diaDaSemana será executado. Se nenhum dos casos coincidir, o bloco dentro do default (se presente) será executado.
O switch em Go oferece uma alternativa concisa e eficaz às estruturas condicionais if e é especialmente útil quando você precisa comparar uma variável com vários valores possíveis. Além disso, diferentemente de algumas outras linguagens, não é necessário usar a instrução break após cada caso, pois o switch em Go automaticamente sai da estrutura após a execução do bloco correspondente.
Importante: todos os valores de case devem ter o mesmo tipo que a expressão switch. Caso contrário, o compilador gerará um erro.
Switch multi-case:
você pode ter vários valores correspondentes em um único caso do switch. Isso é conhecido como "switch multi-case". A ideia é fornecer uma lista de valores que serão tratados de maneira semelhante quando comparados com a expressão do switch. Veja a sintaxe:
switch expressao {
case a, b:
// bloco de código se a expressão for avaliada como a ou b
case c, d:
// bloco de código se a expressão for avaliada como c ou d
case e:
// bloco de código se a expressão for avaliada como e
// ... outros cases, se necessário
default:
// bloco de código se a expressão não for encontrada em nenhum dos cases
}
Aqui está um exemplo de switch multi-case:
dia := "sábado"
switch dia {
case "segunda-feira", "terça-feira", "quarta-feira", "quinta-feira", "sexta-feira":
fmt.Println("Dia útil.")
case "sábado", "domingo":
fmt.Println("Final de semana.")
default:
fmt.Println("Esse dia não existe!")
}
Neste exemplo, o primeiro case inclui vários dias úteis, e o segundo case inclui dois dias de final de semana. Se a expressão dia corresponder a qualquer valor listado em um case, o bloco de código correspondente será executado.
Isso torna o código mais conciso quando você precisa executar a mesma lógica para vários valores. O uso de múltiplos valores em um único caso economiza repetição de código e torna a estrutura do switch mais clara.
17. Loops
A estrutura de repetição for é bastante flexível e pode ser usada de diferentes maneiras. Vamos explorar as formas mais comuns do loop for em Go.
Forma Clássica (sintaxe):
for inicialização; condição; pós-execução {
// código a ser repetido enquanto a condição for verdadeira
}
- inicialização: É executado antes do primeiro loop. Pode ser uma declaração de variável ou uma expressão.
- condição: É avaliada antes de cada iteração. Se for verdadeira, o bloco de código dentro do for é executado; caso contrário, o loop é encerrado.
- pós-execução: É executado após cada iteração.
Veja um exemplo que imprimirá os números de 0 a 4:
for i = 0; i < 5; i++ {
fmt.Println(i)
}
// Explicando o for:
// for i := 0: A variável i é inicializada com o valor 0 no início do loop
// i < 5: 0 loop continuará enquanto a condição i for menor que 5
// i++: Após cada iteração do loop, o valor de i é incrementado em 1
Forma Simplificada:
for condição {
// código a ser repetido enquanto a condição for verdadeira
}
Veja o mesmo exemplo na forma simplificada:
i := 0
for i < 5 {
fmt.Println(i)
i++
}
// i := 0: A variável i é inicializada com o valor ◊ antes do início do loop.
// i < 5: 0 loop continuará enquanto a condição i for menor que 5.
// i++: Após cada iteração do loop, o valor de i é incrementado em 1.
Loop Infinito:
i := 0
for {
fmt.Println(i)
i++
if i : == 6 {
break
}
}
// i := 0: A variável i é inicializada com o valor ◊ antes do início do loop
// for {}: Loop infinito, pois não há condição de parada explicita
// fmt.Println(i):Imprime o valor atual de i
// i++: Após cada iteração do loop, o valor de i é incrementado em 1
// if i == 6: Condição de parada se i for igual a 6
// break: A instrução break encerra imediatamente o loop
A sintaxe é parecida com a forma simplificada, porém, o que muda são as instruções que serão passadas.
Declaração continue:
A instrução continue é usada para pular uma ou mais iterações no loop. Em seguida, continua com a próxima iteração no loop.
Veja um exemplo onde o valor 5 é pulado:
for i:= 0; i <= 10; i++ {
if i == 5 {
continue
}
fmt.Println(i)
}
O if i == 5
verifica se o valor de i
é igual a 5
. Se essa condição for verdadeira, a instrução continue é
executada. A instrução continue pula para a próxima iteração do loop, ignorando o restante do código dentro do loop
que vem após a instrução continue. Portanto, quando i é igual a 5, o bloco fmt.Println(i) é ignorado, e o loop
continua com a próxima iteração. Como resultado, quando você executa esse código, o número 5 não será impresso na saída
do programa, e os outros números de 0 a 10 serão impressos.
Declaração break:
A instrução break é usada para interromper/encerrar a execução do loop. Veja um exemplo onde o loop é interrompido:
for i := 0; i <= 10; i++ {
if i == 5 {
break
}
fmt.Println(i)
}
O if i == 5 verifica se o valor de i é igual a i. Se essa condição for verdadeira, a instrução break é executada. A instrução break encerra imediatamente o loop em que está contida, ignorando o restante do código dentro do loop.
Portanto, quando i atinge o valor 5, a condição if i == 5 se torna verdadeira, e O break é executado, encerrando o loop imediatamente. Isso significa que o bloco fmt.Println(i) não será executado para i igual a 5 e nenhum número maior que 5 será impresso.
Ao executar esse código, você verá a saída incluindo os números de 0 a 4, mas não o 5, porque o loop é encerrado antes de chegar a esse ponto.
Importante: continue e break geralmente são usados com condições.
Loops Aninhados:
Os loops aninhados em Go são uma construção onde um ou mais loops estão contidos dentro de outro loop. Isso é útil quando você precisa iterar sobre elementos em múltiplas dimensões, como em matrizes bidimensionais ou em situações que exigem iterações encadeadas.
Aqui está um exemplo simples de loops aninhados em Go, que imprime um padrão de asteriscos em forma de retângulo:
// Definindo as dimensões do retângulo
largura := 5
altura := 3
// Loop externo para controlar as linhas do retângulo
for i := 0; i < altura; i++ {
// Loop interno para controlar os asteriscos em cada linha
for j = 0; j < largura; j++ {
fmt.Print("* ")
}
fmt.Println() // Adiciona uma quebra de linha após cada linha
}
Este exemplo utiliza dois loops aninhados: o loop externo controla as linhas do retângulo, e o loop interno controla os asteriscos em cada linha.
Os loops aninhados são frequentemente usados para lidar com matrizes bidimensionais, padrões complexos ou qualquer situação em que seja necessário iterar sobre múltiplos conjuntos de dados encadeados.
Palavra-chave range:
A palavra-chave range em Go é usada dentro de loops for para iterar sobre elementos em uma variedade de tipos de dados, como slices, arrays, strings, maps e canais. Ela simplifica o processo de iteração, fornecendo valores do iterável e, opcionalmente, seus índices.
Assim é a sintaxe básica:
for indice, valor := array | slice | map {
// Código a ser executado para cada iteração
}
Veja um exemplo simples utilizando range em uma slice:
// Criando um slice de números
numeros := []int{1, 2, 3, 4, 5}
// Usando range para iterar sobre o slice
for indice, valor := range numeros {
fmt.Printf("Índice: %d, Valor: %d\n", indice, valor)
}
Neste exemplo, o range é utilizado para percorrer o slice numeros. A cada iteração do loop, indice recebe o índice atual e valor recebe o valor correspondente no slice. O código dentro do loop imprime o Índice e o valor.
O range é flexível e pode ser adaptado para diferentes tipos de dados. Além disso, se você não precisar do índice, pode omiti-lo. Por exemplo:
// Criando um slice de números
numeros := []int{1, 2, 3, 4, 5}
for valor := range numeros {
fmt.Println(valor)
}
Neste caso, o "_" é usado para ignorar o índice, focando apenas nos valores. O uso do range proporciona uma maneira concisa de iterar sobre elementos de uma coleção em Go.
18. Functions
Em Go, funções são blocos de código que realizam uma tarefa específica. Elas são fundamentais para organizar e modularizar o código em programas Go. Aqui estão alguns aspectos importantes sobre funções em Go:
Sintaxe Básica:
func nomeDaFunção() {
// Código a ser executado
}
Para criar (geralmente chamada de declaração) uma função, faça o seguinte:
- Use a palavra-chave func;
- Especifique um nome para a função, seguido de parêntesis “()”;
- Adicione o código que define o que a função deve fazer, entre chaves “{}”.
Chamando funções:
Funções não são executadas imediatamente. Elas são "salvas para uso posterior" e serão executadas quando forem chamadas.
No exemplo abaixo, uma função chamada "saudacao()". A chave de abertura ({) indica o início do código da função, e a chave de fechamento (}) indica o fim da função. A função produz a saída "Olá, Mundo!". Para chamar a função, basta escrever o seu nome seguido por dois parênteses "()" dentro da função main:
// Função simples que imprime uma mensagem
func saudacao() {
fmt.Println("Olá, Mundo!")
}
func main() {
// Chamando a função saudacao
saudacao()
}
Também é possível chamar múltiplicas funções, caso deseje:
// Função simples que impre uma mensagem
func saudacao() {
fmt.Println("Olá, Mundo!")
}
func main() {
saudacao() // Chamando a função saudacao (primeira vez)
saudacao() // Chamando a função saudacao (segunda vez)
saudacao() // Chamando a função saudacao (terceira vez)
}
Regras de Nomeação para Funções:
- Um nome de função deve começar com uma letra.
- Um nome de função pode conter apenas caracteres alfanuméricos e sublinhados (A-z, 0-9 e __ );
- Nomes de funções diferenciam maiúsculas de minúsculas;
- Um nome de função não pode conter espaços;
- Se o nome da função consistir em várias palavras, podem ser utilizadas técnicas introduzidas para a nomeação de variáveis com várias palavras.
Dica: Dê à função um nome que reflita o que ela faz.
Parâmetros e Argumentos:
As informações podem ser passadas para funções como parâmetro. Os parâmetros atuam como variáveis dentro da função. Os parâmetros e seus tipos são especificados após o nome da função, entre parênteses. Você pode adicionar quantos parâmetros quiser, basta separá-los com vírgula. Veja a sintaxe:
func nomeDaFuncao (parâmetro1 tipo, parâmetro2 tipp, parâmetro3 tipo) {
// Código a ser executado
}
No exemplo a seguir, existe a função saudacao() com o parâmetro (nome) do tipo string. Quando a função saudacao() é chamada, também passamos um nome (ex: Alice), e o nome é usado dentro da função, que gera vários nomes diferentes com a mesma mensagem ao lado:
// Função que recebe um parâmetro e os imprime 2 func saudacao(nome string) {
func saudacao (nome string) {
fmt.Println("Olá,", nome, "como você está?")
}
func main() {
// Chamando a função com dois argumentos
saudacao("Kage")
saudacao("Gwenhwyfar")
}
Importante: quando um parâmetro é passado para a função, ele chamado de argumento. No exemplo acima, nome é um parâmetro, enquanto Kage e Gwenhwyfar são argumentos.
Múltiplos Parâmetros:
Em Go, você pode definir funções com múltiplos parâmetros para receber diferentes valores durante a chamada da função. Aqui está um exemplo de uma função com múltiplos parâmetros:
// Função que recebe dois parâmetros e os imprime
func saudacao (nome string, idade int) {
fmt.Println("Olá,", nome+"!", "Vi que você tem", idade, "anos!")
}
func main() {
// Chamando a função com dois argumentos
saudacao ("Kage", 15)
saudacao ("Gwenhwyfar", 1000)
}
func saudacao(nome string, idade int): Aqui, está sendo declarada uma função chamada saudacao. Ela recebe dois parâmetros: nome (do tipo string) e idade (do tipo int). A função imprime uma mensagem de saudação junto com o nome e a idade.
saudacao("Kage", 15): A primeira chamada da função saudacao fornece os argumentos "Kage" e 15 para os parâmetros nome e idade, respectivamente. A mensagem de saudação será impressa usando esses valores.
saudacao("Gwenhwyfar", 1000): A segunda chamada da função saudacao fornece os argumentos "Gwenhwyfar" e 1000 para os parâmetros nome e idade, respectivamente. Novamente, a mensagem será impressa usando esses valores.
Importante: ao trabalhar com vários parâmetros, a chamada de função deve ter o mesmo número de argumentos que os parâmetros, e os argumentos devem ser passados na mesma ordem.
Retorno de Valores:
Se quiser que a função retorne um valor, você precisa definir o tipo de dados do valor de retorno (como int, string, etc) e também usar a palavra-chave return dentro da função. Veja a sintaxe:
func NomeDaFuncao(parâmetrol tipo, parâmetro2 tipo) tipoDeRetorno {
// Código a ser executado
return valorDeRetorno
}
Veja um exemplo de função com retorno:
// Função que retorna a soma de dois inteiros
func soma (a, b int) int {
return a + b
}
func main() {
// Imprimindo o resultado
fmt.Println("Resultado da soma:", soma (3, 4))
}
Neste exemplo, a função soma recebe dois parâmetros (a e b) e retorna a soma desses dois valores como um inteiro. A palavra- chave return é usada para indicar que a função está retornando o valor resultante da expressão a + b.
Na função main, a função soma é chamada dentro da função fmt.Println com dois argumentos (3 e 4), cujo a soma é impressa na tela.
Retorno de Valores Nomeados:
Em Go, você também pode nomear os valores de retorno de uma função. Isso é útil quando uma função retorna múltiplos valores e você deseja tornar o código mais legível. Aqui está um exemplo de valores de retorno nomeados:
func minhaFuncao(a, b int) (resultado int) {
resultado = a b
return
}
func main() {
fmt.Println(minhaFuncao(3, 4))
}
Neste exemplo, o valor de retorno foi nomeado como resultado (do tipo int) e o valor é retornado com um retorno simples (significa que a instrução return foi usada sem especificar o nome da variável).
O exemplo acima também pode ser escrito com a variável ao lado do return (ex: return resultado).
Retorno de Valores armazenados numa Variável:
O retorno de valores em Go pode ser armazenado diretamente em variáveis nomeadas na declaração da função. Aqui está um exemplo:
func minhaFuncao(a, b int) (resultado int) {
resultado = a + b
return
}
func main() {
// Chamando a função e armazenando o valor de retorno em uma variável nomeada
somaTotal := minhaFuncao(1, 4)
// Imprimindo o resultado
fmt.Println(somaTotal)
A função minhaFuncao recebe dois parâmetros (a e b) e retorna a soma desses dois números. O valor de retorno é nomeado como resultado.
Na função main, a função minhaFuncao é chamada com os argumentos (1 e 2), e o resultado é armazenado na variável nomeada somaTotal. Em seguida, o valor é impresso na tela.
Múltiplos Valores de Retorno:
É possível retornar múltiplos valores em uma única função. Isso é útil em situações em que você precisa fornecer mais de um resultado. Aqui está um exemplo simples:
// Função que retorna a soma e o produto de dois inteiros
func calcularSomaEProduto(a, b int) (soma, produto int) {
soma = a + b
produto = a * b
return
}
func main() {
// Chamando a função e armazenando os valores de retorno em variáveis nomeadas
resultadoSoma, resultadoProduto := calcularSomaEProduto(3, 4)
// Imprimindo os resultados
fmt.Println("Resultado da Soma:", resultadoSoma)
fmt.Println("Resultado do Produto:", resultadoProduto)
}
O que acontece no exemplo acima:
- A função calcularSomaEProduto recebe dois parâmetros (a e b) e retorna dois valores: a soma e o produto desses dois números.
- Na função main, ao chamar a função, os valores de retorno são armazenados diretamente nas variáveis nomeadas resultadoSoma e resultadoProduto.
- Os valores armazenados são então impressos na tela.
Se, por algum motivo, não quisermos utilizar alguns dos valores retornados de uma função, podemos adicionar um sublinhado ( _ ) para indicar que desejamos omitir esses valores. Essa prática é útil quando estamos cientes dos valores de retorno, mas não temos a intenção de utilizá-los na lógica subsequente do programa.
Quando você usa o sublinhado ( _ ), está indicando ao compilador que você não tem interesse em armazenar ou utilizar esse valor de retorno específico. Veja um exemplo:
// Função que retorna a soma e o produto de dois inteiros
func calcularSomaEProduto(a, b int) (soma, produto int) {
soma = a + b
produto = a * b
return
}
func main() {
// Chamando a função e utilizando a omissão de valor para ignorar o resultado do produto
resultadoSoma, _ := calcularSomaEProduto(3, 4)
// Imprimindo apenas o resultado da soma
fmt.Println("Resultado da Soma:", resultadoSoma)
}
Neste exemplo, o valor de retorno do produto é ignorado usando (_) no lado esquerdo da atribuição. Isso é útil quando você está ciente do valor de retorno, mas não tem intenção de usá-lo na lógica subsequente do programa.
Funções Recursivas:
Em Go, é possível criar funções recursivas, ou seja, funções que chamam a si mesmas. Aqui está um exemplo simples de uma função recursiva que calcula o fatorial de um número:
// Função recursiva para imprimir números de 1 a 5
func contagem(x int) {
// Caso base: quando x atinge 6, a recursão para
if x == 6 {
return
}
// Imprimindo o valor de x
fmt.Println(x)
// Chamada recursiva: incrementa x e continua a recursão
contagem(x + 1)
}
func main() {
// Chamando a função para iniciar a contagem de 1 a 5
contagem(1)
}
No exemplo a seguir, contagem() é uma função que se chama recursivamente (ou seja, chama a si mesma). Utilizamos a variável x como os dados, os quais incrementam em 1 (x + 1) a cada chamada recursiva. A recursão é encerrada quando a variável x é igual a 6 (x == 6).
A recursão é um conceito comum em matemática e programação. Isso oferece a vantagem de poder percorrer dados para atingir um resultado.
O desenvolvedor deve ter cuidado ao lidar com funções recursivas, pois é relativamente fácil cair na armadilha de escrever uma função que nunca termina ou que consome quantidades excessivas de memória ou poder de processamento. No entanto, quando escrita corretamente, a recursão pode ser uma abordagem muito eficiente e matematicamente elegante para programar.
Um exemplo de uma função recursiva que calcula o fatorial:
// Função recursiva para calcular o fatorial de um número
func fatorial(n int) int {
// Caso base: fatorial de 0 é 1
if n == 0 {
return 1
}
// Chamada recursiva: fatorial(n) = n * fatorial(n-1)
return n * fatorial(n-1)
}
func main() {
// Chamando a função para calcular o fatorial de 5
resultado := fatorial(5)
// Imprimindo o resultado
fmt.Println("O fatorial de 5 é:", resultado)
}
A função fatorial é definida para calcular o fatorial de um número n. A condição if n == 0 é o caso base, que retorna 1 quando n é igual a zero.
A chamada recursiva return n* fatorial(n-1) ocorre para valores diferentes de zero, calculando o fatorial de forma recursiva.
19. Structs
Em Go, uma struct (estrutura) é um tipo de dado composto que agrupa diferentes tipos de dados sob um único nome. Ela é frequentemente utilizada para modelar conceitos mais complexos, representando uma coleção de campos nomeados, onde cada campo pode ter um tipo de dado diferente. As structs em Go são uma forma de organizar e agrupar dados relacionados.
Enquanto arrays são usados para armazenar vários valores do mesmo tipo de dados em uma única variável, structs são usadas para armazenar vários valores de diferentes tipos de dados em uma única variável. Cada campo dentro de uma struct pode ter um tipo de dado diferente, e os campos são acessados por meio de seus nomes.
A sintaxe básica de uma struct em Go é a seguinte:
type NomeDaStruct struct {
Campol Tipol
Campo2 Tipo2
// ... Outros campos e tipos (depende da necessidade)
CampoN TipoN
}
NomeDaStruct: É o nome da struct. Campo1, Campo2, ..., CampoN: São os nomes dos campos na struct. Tipo1, Tipo2, ..., TipoN: São os tipos de dados dos respectivos campos.
Aqui está um exemplo básico de uma struct em Go:
// Definindo uma struct chamada Pessoa
type Pessoa struct {
Nome string
Idade int
Altura float64
}
func main() {
// Criando uma instância da struct Pessoa
pessoa1 := Pessoa{
Nome: "Gwenhwyfar",
Idade: 1000,
Altura: 1.65,
}
// Acessando os campos da struct
fmt.Println("Nome:", pessoal. Nome)
fmt.Println("Idade:", pessoal. Idade)
fmt.Println("Altura:", pessoal. Altura)
}
Neste exemplo: Pessoa é uma struct que possui três campos: Nome (string), Idade (int), e Altura (float64).
Uma instância da struct é criada com valores específicos para cada campo.
Os campos da struct são acessados usando a notação de ponto (pessoa1.Nome, pessoal.Idade, pessoa1.Altura).
Passando Struct como Argumento de Função:
Em Go, você pode passar uma struct como argumento para uma função da mesma maneira que faria com qualquer outro tipo de dado. Aqui está um exemplo simples:
// Definindo uma struct chamada Pessoa
type Pessoa struct {
Nome string
Idade int
Altura float64
}
// Função que recebe uma struct como argumento ]
func imprimirPessoa(p Pessoa) {
fmt.Println("Nome:", p. Nome)
fmt.Println("Idade:", p.Idade)
fmt.Println("Altura:", p. Altura)
}
func main() {
// Criando uma instância da struct Pessoa
pessoa1 := Pessoa{
Nome: "Gwenhwyfar",
Idade: 1000,
Altura: 1.65,
}
// Chamando a função e passando a struct como argumento
imprimirPessoa(pessoa1)
}
Neste exemplo, a função imprimirPessoa recebe uma struct do tipo Pessoa como argumento e imprime os valores dos campos dessa struct. Ao chamar a função, você passa uma instância da struct como argumento, neste caso, pessoa1.
20. Maps
Um map em Go é uma estrutura de dados que facilita a associação de valores a chaves, proporcionando uma implementação eficiente de tabelas de hash ou dicionários comuns em outras linguagens de programação. Essa estrutura consiste em uma coleção dinâmica de pares chave-valor, onde cada chave é única dentro do map e está vinculada a um valor específico.
Os maps em Go são dinâmicos, permitindo a adição, remoção e atualização de pares chave-valor durante a execução do programa. Além disso, os tipos de dados das chaves e dos valores podem variar, proporcionando flexibilidade na manipulação de diferentes tipos de informações.
O acesso aos valores associados a uma chave é efetuado de forma rápida e direta, proporcionando um acesso eficiente aos dados armazenados. Além disso, é possível verificar a existência de uma chave no map utilizando a atribuição múltipla, como em valor, chaveExiste := meuMapa["chave"].
Em resumo, os maps são uma ferramenta poderosa em Go, oferecendo uma maneira flexível e eficiente de organizar e manipular dados associados, sendo particularmente úteis em situações onde é necessário indexar e recuperar valores com base em chaves únicas.
Veja a sintaxe básica para criar um map, tanto com var quanto com 0 ":=":
// Utilizando a palavra-chave 'var' para declarar e inicializar um map
var a = map[TipoDaChave] TipoDoValor{chavel: value1, chave2: valor2,...}
// Utilizando a declaração curta ':=' para declarar e inicializar um map
b := map[TipoDaChave]TipoDoValor{chavel:valor1, chave2:valor2,...}
Este exemplo mostra como criar mapas em Go:
func main() {
var a = map[string]string{"marca": "Fiat", "modelo": "Uno", "ano": "2007"}
b := map[string]int{"Bahia": 1, "São Paulo": 2, "Amapá": 3, "Santa Catarina": 4}
fmt.Printf("a\t%v\n", a)
fmt.Printf("b\t%v\n", b)
}
No map a, as chaves são strings representando informações sobre um carro (marca, modelo e ano), e os valores associados a essas chaves são também strings.
No map b, as chaves são strings representando estados brasileiros, e os valores associados a essas chaves são inteiros representando algum tipo de identificador.
As linhas fmt.Printf são utilizadas para imprimir os maps formatados. %v é um indicador de formato que exibe os valores de uma forma padrão. No contexto do código, isso imprimirá os maps a e b na saída padrão. O uso de tabs (\t) na string de formato ajuda a alinhar a saída para melhor legibilidade.
Importante: A ordem dos elementos do mapa definidos no código é diferente da forma como são armazenados. Os dados são armazenados de forma que haja uma recuperação eficiente dos dados do mapa.
Criando Mapas usando a Função make():
É possível criar mapas com a função make(). Veja a sintaxe:
var a = make (map [TipoDaChave] TipoDoValor)
b := make(map[TipoDaChave] TipoDoValor)
Agora, veja um exemplo de um mapa usando a função make():
func main() {
// Criando um map vazio de strings para strings
make (map[string]string) // 0 mapa está vazio agora
// Adicionando valores ao map 'a'
a["marca"] = "Fiat"
a["modelo"] = "Uno"
a["ano"] = "2007"
// Agora, 'a' não está mais vazio
// Criando um map vazio de strings para inteiros
b := make(map[string]int)
b["Bahia"] = 1
b["São Paulo"] = 2
b["Amapá"] = 3
b["Santa Catarina"] = 4
// Imprimindo os maps formatados
fmt.Printf("a\t%v\n", a)
fmt.Printf("b\t%v\n", b)
}
Criando um Mapa Vazio:
Existem duas maneiras de criar um mapa vazio. Uma é usando a função make() e a outra é usando a seguinte sintaxe:
var a map [KeyType] ValueType
Importante: A função make() é a maneira correta de criar um mapa vazio. Se você criar um mapa vazio de uma maneira diferente e escrever nele, isso causará pânico no tempo de execução.
Este exemplo mostra a diferença entre declarar um mapa vazio usando com a função make() e sem ela:
var a = make(map[string]string)
var b map[string] string
fmt.Println(a == nil) // Retorna false (falso)
fmt.Println(b == nil) // Retorna true (verdadeiro)
A palavra-chave nil em Go é utilizada para representar a ausência de valor ou a falta de inicialização de um ponteiro, slice, map, channel, ou interface. Neste caso, nil está relacionada à verificação de inicialização dos mapas. Entenda:
var a = make(map[string]string): o mapa (a) foi criado usando a função make. Mesmo que o mapa esteja vazio, ele é inicializado internamente, apontando para uma estrutura de dados adequada. Portanto, a não é nil.
var b map[string]string: Neste caso, uma variável (b) do tipo mapa está sendo declarada, mas não está inicializando. Isso significa que b é nil por padrão, pois não foi atribuído nenhum valor.
Tipos de chave permitidos: A chave do mapa pode ser de qualquer tipo de dados para o qual o operador de igualdade (==) esteja definido. Esses incluem:
- Booleanos;
- Números;
- Strings;
- Arrays;
- Ponteiros;
- Structs;
- Interfaces (desde que o tipo dinâmico suporte igualdade).
Os tipos de chave inválidos são:
- Slices;
- Maps;
- Functions.
Esses tipos são inválidos porque o operador de igualdade (==) não está definido para eles.
Importante: os valores dos mapas podem ser de qualquer tipo.
Acessar Elementos do Mapa:
Para acessar elementos de um mapa em Go, você utiliza a chave correspondente ao elemento desejado dentro de colchetes “[ ]”. Veja um exemplo:
func main() {
var a = make(map[string] string)
a["marca"] = "Fiat"
a["modelo"] = "Uno"
a["ano"] = "2007"
fmt.Printf(a["marca"]) // Será exibido apenas o nome da marca
}
Atualizar e Adicionar Elementos a um Mapa:
É possível atualizar e adicionar elementos num mapa, veja:
func main() {
var a = make(map[string] string)
a["marca"] = "Fiat"
a["modelo"] = "Uno"
a["ano"] = "2007"
fmt.Println(a) // Aqui, o valor de "ano" será mostrado
a["ano"] = "2013" // Atualizando um elemento
a["cor"] = "cinza" // Adicionando um elemento
fmt.Println(a) // Aqui, o valor novo de "ano" será mostrado, e você pode ver a nova cor que foi adicionada
}
Remover Elementos de um Mapa:
A remoção de elementos de um mapa é feita usando a função delete(). Veja como é a sintaxe:
delete (nomeDoMapa, chave)
Exemplo:
func main() {
var a = make(map[string] string)
a["marca"] = "Fiat"
a["modelo"] = "Uno"
a["ano"] = "2007"
fmt.Println(a) // Aqui, o valor antigo de "ano" será mostrado
delete(a, "modelo") // Removendo um elemento
fmt.Println(a) // Aqui, o valor novo do "ano" e da "marca" será mostrado
}
Verificando Elementos Específicos em um Mapa:
Você pode verificar se uma determinada chave existe em um mapa usando:
val, val, ok :=NomeDoMapa [ chave]
Se você deseja apenas verificar a existência de uma determinada chave, pode usar o identificador em branco (_) no lugar de val. Veja o exemplo:
func main() {
var a = map[string]string{"marca": "Fiat", "modelo": "Uno", "ano": "2007", "dia":""}
vall, ok1 := a["marca"] // Verificando a chave existente e seu valor
val2, ok2 := a["cor"] // Verificando chave inexistente e seu valor
val3, ok3 := a["dia"] // Verificando a chave existente e seu valor
_, ok4 := a["modelo"] // Verificando apenas a chave existente e não o seu valor
fmt.Println(vall, ok1)
fmt.Println(val2, ok2)
fmt.Println(val3, ok3)
fmt.Println(ok4)
}
Neste exemplo, verificamos a existência de diferentes chaves no mapa.
A chave "cor" não existe no mapa. Portanto, o valor é uma string vazia (“”).
A variável ok2 é usada para descobrir se a chave existe ou não. Porque teríamos obtido o mesmo valor se o valor da chave "color" estivesse vazio. Este é o caso de val3.
Conclusão e Agradecimento
Embora essa introdução tenha fornecido uma base sólida para entender o básico da linguagem Go, é importante destacar que o universo dessa linguagem é é vasto e cheio de recursos interessantes. Esta breve exploração é apenas o ponto de partida. Incentivo a todos a continuarem aprofundando seus conhecimentos, explorando temas mais avançados e buscando por mais recursos disponíveis. A jornada no aprendizado de Go é uma oportunidade para se aprimorar continuamente. Há sempre mais para descobrir e aprender, então não se limitem a este conteúdo. Explorem, pratiquem e expandam suas habilidades em Go!
Vale lembrar que não sou o dono da verdade, o conteúdo aqui pode estar desatualizado, ter maneiras melhores de serem abordadas e até mesmo, possuem metodologias mais sólidas, isso dependente do tempo em que está lendo isso. Mas obrigado pelo apoio e até a próxima!