Desafios práticos — Introdução ao Go
Resolva cada atividade antes de abrir a solução. Os desafios estão organizados por tema e avançam gradualmente. Em atividades com vários arquivos, crie cada arquivo indicado nos comentários, separando os trechos de código. As soluções utilizam apenas os recursos apresentados na introdução; o exercício de dependências externas exige conexão para instalar o pacote.
Tema 1 — Primeiros programas, tipos e formatação
Atividade 1
Crie um programa com package main, importe fmt e mostre a mensagem Olá, Go! no terminal.
Resultado esperado: A saída deve conter exatamente Olá, Go!.
Clique para ver como resolver!
package main
import "fmt"
func main() {
fmt.Println("Olá, Go!")
}
Atividade 2
Declare as variáveis nome e idade, usando respectivamente string e int. Mostre os dois valores com fmt.Printf.
Resultado esperado: Com nome = "Ana" e idade = 25, a saída deve ser Ana tem 25 anos..
Clique para ver como resolver!
package main
import "fmt"
func main() {
nome := "Ana"
idade := 25
fmt.Printf("%s tem %d anos.\n", nome, idade)
}
Atividade 3
Declare, sem inicializar, variáveis dos tipos int, float64, bool e string. Mostre os valores entre delimitadores para identificar os valores zero.
Resultado esperado: Devem aparecer 0, 0, false e uma string vazia.
Clique para ver como resolver!
package main
import "fmt"
func main() {
var numero int
var decimal float64
var ativo bool
var texto string
fmt.Printf("int=%d float=%.1f bool=%t string=[%s]\n", numero, decimal, ativo, texto)
}
Atividade 4
Declare um rune contendo ç e um byte contendo a letra A. Mostre os caracteres e seus valores numéricos.
Resultado esperado: Os caracteres devem ser ç e A; o código Unicode de ç é 231 e o byte ASCII de A é 65.
Clique para ver como resolver!
package main
import "fmt"
func main() {
letra := 'ç'
var ascii byte = 'A'
fmt.Printf("rune: %c (%d)\n", letra, letra)
fmt.Printf("byte: %c (%d)\n", ascii, ascii)
}
Atividade 5
Calcule a divisão de 7 por 2 como números inteiros e depois como números decimais, convertendo pelo menos um dos operandos.
Resultado esperado: A divisão inteira deve resultar em 3, enquanto a divisão com conversão deve resultar em 3.5.
Clique para ver como resolver!
package main
import "fmt"
func main() {
a, b := 7, 2
inteiro := a / b
decimal := float64(a) / float64(b)
fmt.Println("Inteira:", inteiro)
fmt.Println("Decimal:", decimal)
}
Tema 2 — Variáveis, constantes e operadores
Atividade 6
Crie uma variável tentativas iniciada em 0. Incremente seu valor três vezes e mostre o resultado.
Resultado esperado: O valor final deve ser 3.
Clique para ver como resolver!
package main
import "fmt"
func main() {
tentativas := 0
tentativas++
tentativas++
tentativas++
fmt.Println(tentativas)
}
Atividade 7
Crie uma constante limite igual a 100, representando a capacidade máxima, e uma variável uso igual a 37, representando a capacidade em uso. Calcule e exiba a capacidade disponível.
Resultado esperado: O resultado deve ser 63.
Clique para ver como resolver!
package main
import "fmt"
func main() {
const limite = 100
uso := 37
restante := limite - uso
fmt.Println("Disponível:", restante)
}
Atividade 8
Use um bloco const com iota para criar três estados: Pendente, Executando e Concluido. Mostre os valores.
Resultado esperado: Os estados devem receber, nessa ordem, os valores 0, 1 e 2.
Clique para ver como resolver!
package main
import "fmt"
const (
Pendente = iota
Executando
Concluido
)
func main() {
fmt.Println(Pendente, Executando, Concluido)
}
Atividade 9
Receba um número fixado no código (17) e descubra se ele é par ou ímpar usando o operador %.
Resultado esperado: Para 17, a mensagem deve ser ímpar. Ao substituir por 18, deve ser par.
Clique para ver como resolver!
package main
import "fmt"
func main() {
numero := 17
if numero%2 == 0 {
fmt.Println("par")
} else {
fmt.Println("ímpar")
}
}
Atividade 10
Simule um consumo inicial de 10 unidades, some 5 usando um operador de atribuição e depois subtraia 3 da mesma forma.
Resultado esperado: O consumo final deve ser 12.
Clique para ver como resolver!
package main
import "fmt"
func main() {
consumo := 10
consumo += 5
consumo -= 3
fmt.Println(consumo)
}
Tema 3 — Condições, switch e laços de repetição
Atividade 11
Crie uma variável nota e classifique-a com if, else if e else: abaixo de 5 é Reprovado; de 5 até menos de 7 é Recuperação; a partir de 7 é Aprovado.
Resultado esperado: Com nota = 6, a saída deve ser Recuperação.
Clique para ver como resolver!
package main
import "fmt"
func main() {
nota := 6.0
if nota < 5 {
fmt.Println("Reprovado")
} else if nota < 7 {
fmt.Println("Recuperação")
} else {
fmt.Println("Aprovado")
}
}
Atividade 12
Verifique se um usuário pode acessar um sistema somente quando autenticado e ativo forem verdadeiros.
Resultado esperado: Com autenticado = true e ativo = false, o acesso deve ser negado.
Clique para ver como resolver!
package main
import "fmt"
func main() {
autenticado := true
ativo := false
if autenticado && ativo {
fmt.Println("Acesso permitido")
} else {
fmt.Println("Acesso negado")
}
}
Atividade 13
Use switch para transformar uma severidade numérica (1, 2 ou 3) em INFO, WARN ou ERROR. Inclua um caso padrão.
Resultado esperado: Para 2, a saída deve ser WARN; qualquer valor desconhecido deve imprimir INVÁLIDO.
Clique para ver como resolver!
package main
import "fmt"
func main() {
nivel := 2
switch nivel {
case 1:
fmt.Println("INFO")
case 2:
fmt.Println("WARN")
case 3:
fmt.Println("ERROR")
default:
fmt.Println("INVÁLIDO")
}
}
Atividade 14
Percorra os números de 1 a 10, ignore os pares com continue e pare a execução quando o valor chegar a 8 com break.
Resultado esperado: A saída deve conter apenas 1, 3, 5 e 7.
Clique para ver como resolver!
package main
import "fmt"
func main() {
for numero := 1; numero <= 10; numero++ {
if numero == 8 {
break
}
if numero%2 == 0 {
continue
}
fmt.Println(numero)
}
}
Atividade 15
Percorra um slice de números usando for range e calcule a soma de todos os elementos.
Resultado esperado: Para []int{3, 4, 5}, a soma deve ser 12.
Clique para ver como resolver!
package main
import "fmt"
func main() {
numeros := []int{3, 4, 5}
total := 0
for _, numero := range numeros {
total += numero
}
fmt.Println("Soma:", total)
}
Tema 4 — Funções, retornos, erros e defer
Atividade 16
Escreva uma função multiplicar que receba dois int e retorne a multiplicação. Chame a função em main.
Resultado esperado: Para os argumentos 6 e 7, o resultado deve ser 42.
Clique para ver como resolver!
package main
import "fmt"
func multiplicar(a, b int) int {
return a * b
}
func main() {
fmt.Println(multiplicar(6, 7))
}
Atividade 17
Crie uma função resumo que receba dois inteiros e retorne dois valores: a soma e a diferença.
Resultado esperado: Para (10, 4), a soma deve ser 14 e a diferença 6.
Clique para ver como resolver!
package main
import "fmt"
func resumo(a, b int) (int, int) {
return a + b, a - b
}
func main() {
soma, diferenca := resumo(10, 4)
fmt.Println("Soma:", soma)
fmt.Println("Diferença:", diferenca)
}
Atividade 18
Implemente uma função dividir que retorne (float64, error). Se o divisor for zero, use fmt.Errorf; caso contrário, devolva o resultado.
Resultado esperado: Ao dividir 10 por 0, deve aparecer uma mensagem de erro sem interromper o programa com panic.
Clique para ver como resolver!
package main
import "fmt"
func dividir(a, b float64) (float64, error) {
if b == 0 {
return 0, fmt.Errorf("divisão por zero")
}
return a / b, nil
}
func main() {
resultado, err := dividir(10, 0)
if err != nil {
fmt.Println("Erro:", err)
return
}
fmt.Println(resultado)
}
Atividade 19
Crie uma função que use dois defer para mostrar a ordem em que são executados. Mostre uma mensagem antes do retorno.
Resultado esperado: A mensagem normal deve aparecer primeiro; os dois defer devem executar em ordem inversa.
Clique para ver como resolver!
package main
import "fmt"
func exemplo() {
defer fmt.Println("Primeiro defer registrado")
defer fmt.Println("Segundo defer registrado")
fmt.Println("Executando função")
}
func main() {
exemplo()
}
Atividade 20
Crie validarIdade(idade int) (string, error). Idades negativas devem retornar erro; menores de 18 devem retornar Menor de idade; os demais, Maior de idade.
Resultado esperado: Ao testar -1, 16 e 20, devem aparecer, nessa ordem, um erro, Menor de idade e Maior de idade.
Clique para ver como resolver!
package main
import "fmt"
func validarIdade(idade int) (string, error) {
if idade < 0 {
return "", fmt.Errorf("idade inválida: %d", idade)
}
if idade < 18 {
return "Menor de idade", nil
}
return "Maior de idade", nil
}
func main() {
for _, idade := range []int{-1, 16, 20} {
mensagem, err := validarIdade(idade)
if err != nil {
fmt.Println("Erro:", err)
continue
}
fmt.Println(mensagem)
}
}
Tema 5 — Arrays, slices e matrizes
Atividade 21
Crie um array de três inteiros, altere o segundo elemento e mostre o array e seu comprimento.
Resultado esperado: Partindo de [10, 20, 30], o array final deve ser [10 99 30] e len deve valer 3.
Clique para ver como resolver!
package main
import "fmt"
func main() {
numeros := [3]int{10, 20, 30}
numeros[1] = 99
fmt.Println(numeros)
fmt.Println("Tamanho:", len(numeros))
}
Atividade 22
Crie um slice com make, comprimento zero e capacidade cinco. Adicione três valores e mostre len e cap.
Resultado esperado: Após adicionar 10, 20 e 30, len deve ser 3 e cap deve ser 5.
Clique para ver como resolver!
package main
import "fmt"
func main() {
numeros := make([]int, 0, 5)
numeros = append(numeros, 10, 20, 30)
fmt.Println(numeros)
fmt.Println("len:", len(numeros), "cap:", cap(numeros))
}
Atividade 23
Copie os elementos de um slice para outro usando copy. Modifique somente o primeiro elemento da cópia.
Resultado esperado: Depois da alteração, o original deve continuar [1 2 3], e a cópia deve ser [99 2 3].
Clique para ver como resolver!
package main
import "fmt"
func main() {
original := []int{1, 2, 3}
copia := make([]int, len(original))
copy(copia, original)
copia[0] = 99
fmt.Println("Original:", original)
fmt.Println("Cópia:", copia)
}
Atividade 24
Remova o elemento de índice 2 de um slice sem alterar a ordem dos demais elementos.
Resultado esperado: Para [10 20 30 40 50], o resultado deve ser [10 20 40 50].
Clique para ver como resolver!
package main
import "fmt"
func main() {
numeros := []int{10, 20, 30, 40, 50}
indice := 2
numeros = append(numeros[:indice], numeros[indice+1:]...)
fmt.Println(numeros)
}
Atividade 25
Monte um slice multidimensional com duas linhas de números e calcule a soma de todos os valores usando dois for range.
Resultado esperado: Com as linhas [1, 2, 3] e [4, 5, 6], o total deve ser 21.
Clique para ver como resolver!
package main
import "fmt"
func main() {
matriz := [][]int{{1, 2, 3}, {4, 5, 6}}
total := 0
for _, linha := range matriz {
for _, numero := range linha {
total += numero
}
}
fmt.Println("Total:", total)
}
Tema 6 — Maps e ordenação de dados
Atividade 26
Crie um map contendo os serviços ssh e http e suas portas. Acrescente https, remova http e confirme a presença de ssh usando valor, ok.
Resultado esperado: O serviço ssh deve existir na porta 22, e http não deve mais existir.
Clique para ver como resolver!
package main
import "fmt"
func main() {
portas := map[string]int{"ssh": 22, "http": 80}
portas["https"] = 443
delete(portas, "http")
porta, existe := portas["ssh"]
_, httpExiste := portas["http"]
fmt.Println("ssh:", porta, existe)
fmt.Println("http existe:", httpExiste)
}
Atividade 27
Conte quantas vezes cada nome aparece em um slice usando um map.
Resultado esperado: Para Ana, Bia, Ana, Caio, Bia, Ana, as contagens devem ser Ana=3, Bia=2 e Caio=1.
Clique para ver como resolver!
package main
import "fmt"
func main() {
nomes := []string{"Ana", "Bia", "Ana", "Caio", "Bia", "Ana"}
contagem := make(map[string]int)
for _, nome := range nomes {
contagem[nome]++
}
fmt.Println("Ana:", contagem["Ana"])
fmt.Println("Bia:", contagem["Bia"])
fmt.Println("Caio:", contagem["Caio"])
}
Atividade 28
Ordene um slice de números inteiros em ordem crescente usando o package slices.
Resultado esperado: O slice [8, 3, 10, 1] deve ficar [1 3 8 10].
Clique para ver como resolver!
package main
import (
"fmt"
"slices"
)
func main() {
numeros := []int{8, 3, 10, 1}
slices.Sort(numeros)
fmt.Println(numeros)
}
Atividade 29
Crie uma struct Processo com nome e consumo de memória, forme um slice e ordene os processos do maior para o menor consumo.
Resultado esperado: Os processos de 300, 150 e 80 MB devem aparecer nessa ordem, independentemente da ordem inicial.
Clique para ver como resolver!
package main
import (
"fmt"
"sort"
)
type Processo struct {
Nome string
Memoria int
}
func main() {
processos := []Processo{
{"nginx", 80}, {"postgres", 300}, {"sshd", 150},
}
sort.Slice(processos, func(i, j int) bool {
return processos[i].Memoria > processos[j].Memoria
})
for _, processo := range processos {
fmt.Println(processo.Nome, processo.Memoria)
}
}
Atividade 30
Armazene em um map do tipo map[string]any uma string, um inteiro e um booleano. Percorra três chaves em ordem definida e identifique o tipo de cada valor usando type switch.
Resultado esperado: A saída deve identificar nome como string, tentativas como inteiro e ativo como booleano.
Clique para ver como resolver!
package main
import "fmt"
func main() {
dados := map[string]any{"nome": "Ana", "tentativas": 3, "ativo": true}
for _, chave := range []string{"nome", "tentativas", "ativo"} {
switch valor := dados[chave].(type) {
case string:
fmt.Println(chave, "string:", valor)
case int:
fmt.Println(chave, "int:", valor)
case bool:
fmt.Println(chave, "bool:", valor)
}
}
}
Tema 7 — Structs e métodos
Atividade 31
Crie uma struct Servidor com os campos Nome, IP e Ativo. Inicialize apenas os dois primeiros e mostre todos os campos.
Resultado esperado: O campo Ativo, não inicializado, deve aparecer como false.
Clique para ver como resolver!
package main
import "fmt"
type Servidor struct {
Nome string
IP string
Ativo bool
}
func main() {
servidor := Servidor{Nome: "srv01", IP: "192.0.2.10"}
fmt.Printf("Nome=%s IP=%s Ativo=%t\n", servidor.Nome, servidor.IP, servidor.Ativo)
}
Atividade 32
Crie um slice com três structs Servidor, cada uma com nome e estado. Conte quantos servidores estão ativos.
Resultado esperado: Para dois ativos e um inativo, o total deve ser 2.
Clique para ver como resolver!
package main
import "fmt"
type Servidor struct {
Nome string
Ativo bool
}
func main() {
servidores := []Servidor{
{Nome: "a", Ativo: true},
{Nome: "b", Ativo: false},
{Nome: "c", Ativo: true},
}
total := 0
for _, servidor := range servidores {
if servidor.Ativo {
total++
}
}
fmt.Println("Ativos:", total)
}
Atividade 33
Adicione à struct Servidor um método Resumo() que retorne uma string com nome e IP, usando fmt.Sprintf.
Resultado esperado: Para srv01 e 192.0.2.10, deve retornar srv01 (192.0.2.10).
Clique para ver como resolver!
package main
import "fmt"
type Servidor struct {
Nome string
IP string
}
func (s Servidor) Resumo() string {
return fmt.Sprintf("%s (%s)", s.Nome, s.IP)
}
func main() {
servidor := Servidor{"srv01", "192.0.2.10"}
fmt.Println(servidor.Resumo())
}
Atividade 34
Crie uma struct Conta com Saldo e um método Depositar(valor float64) error que modifique o saldo usando receiver por ponteiro e rejeite valores não positivos.
Resultado esperado: Um depósito de 50 em saldo 100 deve resultar em 150; um depósito de -10 deve retornar erro e não mudar o saldo.
Clique para ver como resolver!
package main
import "fmt"
type Conta struct {
Saldo float64
}
func (c *Conta) Depositar(valor float64) error {
if valor <= 0 {
return fmt.Errorf("depósito inválido: %.2f", valor)
}
c.Saldo += valor
return nil
}
func main() {
conta := Conta{Saldo: 100}
fmt.Println("Primeiro depósito:", conta.Depositar(50))
fmt.Println("Segundo depósito:", conta.Depositar(-10))
fmt.Println("Saldo:", conta.Saldo)
}
Atividade 35
Crie um tipo próprio Temperatura baseado em float64 e um método Fahrenheit() para converter Celsius em Fahrenheit.
Resultado esperado: Para Temperatura(25), o método deve retornar 77.
Clique para ver como resolver!
package main
import "fmt"
type Temperatura float64
func (t Temperatura) Fahrenheit() float64 {
return float64(t)*9/5 + 32
}
func main() {
temperatura := Temperatura(25)
fmt.Println(temperatura.Fahrenheit())
}
Tema 8 — Interfaces, type assertion e type switch
Atividade 36
Defina a interface Notificador com Notificar(string). Faça as structs Email e Console implementarem esse método, sem declarar explicitamente que implementam a interface.
Resultado esperado: Uma função que recebe Notificador deve conseguir enviar a mesma mensagem por Email e Console.
Clique para ver como resolver!
package main
import "fmt"
type Notificador interface {
Notificar(mensagem string)
}
type Email struct{ Endereco string }
type Console struct{}
func (e Email) Notificar(mensagem string) {
fmt.Println("Email para", e.Endereco+":", mensagem)
}
func (Console) Notificar(mensagem string) {
fmt.Println("Console:", mensagem)
}
func enviar(n Notificador, mensagem string) {
n.Notificar(mensagem)
}
func main() {
enviar(Email{Endereco: "ana@exemplo.com"}, "Olá")
enviar(Console{}, "Olá")
}
Atividade 37
Crie uma interface Equipamento que exija os métodos Ligar() e Desligar(). Implemente ambos em uma struct Roteador e escreva uma função que os invoque.
Resultado esperado: Os dois métodos devem ser executados. Um tipo com apenas um deles não deve satisfazer a interface.
Clique para ver como resolver!
package main
import "fmt"
type Equipamento interface {
Ligar()
Desligar()
}
type Roteador struct{ Nome string }
func (r Roteador) Ligar() { fmt.Println(r.Nome, "ligado") }
func (r Roteador) Desligar() { fmt.Println(r.Nome, "desligado") }
func operar(e Equipamento) {
e.Ligar()
e.Desligar()
}
func main() {
operar(Roteador{Nome: "R1"})
}
Atividade 38
Crie uma interface Medidor que exija Medir() int e implemente-a em duas structs diferentes. Some as medidas percorrendo um slice dessa interface.
Resultado esperado: Com medidas 30 e 12, o total deve ser 42.
Clique para ver como resolver!
package main
import "fmt"
type Medidor interface {
Medir() int
}
type Memoria struct{ Uso int }
type Disco struct{ Uso int }
func (m Memoria) Medir() int { return m.Uso }
func (d Disco) Medir() int { return d.Uso }
func main() {
medidores := []Medidor{Memoria{Uso: 30}, Disco{Uso: 12}}
total := 0
for _, medidor := range medidores {
total += medidor.Medir()
}
fmt.Println("Total:", total)
}
Atividade 39
Armazene um valor em uma variável do tipo any e use type assertion com a forma valor, ok para tentar recuperá-lo como int.
Resultado esperado: Com o texto "42", a conversão por assertion para int deve falhar com ok = false, sem panic.
Clique para ver como resolver!
package main
import "fmt"
func main() {
var dado any = "42"
numero, ok := dado.(int)
fmt.Println("Número:", numero)
fmt.Println("É int:", ok)
}
Atividade 40
Crie uma função descrever(valor any) que use type switch para reconhecer int, string e bool, incluindo um caso padrão.
Resultado esperado: Ao enviar 7, "Go", true e 3.14, a função deve identificar os três primeiros e dizer que o último é desconhecido.
Clique para ver como resolver!
package main
import "fmt"
func descrever(valor any) {
switch v := valor.(type) {
case int:
fmt.Println("Inteiro:", v)
case string:
fmt.Println("Texto:", v)
case bool:
fmt.Println("Booleano:", v)
default:
fmt.Println("Tipo desconhecido")
}
}
func main() {
for _, valor := range []any{7, "Go", true, 3.14} {
descrever(valor)
}
}
Tema 9 — Composição e ponteiros
Atividade 41
Modele um Endereco e uma Pessoa que contenha um campo nomeado Endereco. Exiba a cidade usando o acesso aos campos.
Resultado esperado: Para uma pessoa em Recife, o programa deve imprimir Recife.
Clique para ver como resolver!
package main
import "fmt"
type Endereco struct{ Cidade string }
type Pessoa struct {
Nome string
Endereco Endereco
}
func main() {
pessoa := Pessoa{Nome: "Ana", Endereco: Endereco{Cidade: "Recife"}}
fmt.Println(pessoa.Endereco.Cidade)
}
Atividade 42
Crie uma struct Auditoria com um método Registrar() e incorpore-a como campo embutido em Servico. Chame o método diretamente pela struct externa.
Resultado esperado: A chamada servico.Registrar() deve funcionar devido à promoção de métodos.
Clique para ver como resolver!
package main
import "fmt"
type Auditoria struct{}
func (Auditoria) Registrar() {
fmt.Println("Registro criado")
}
type Servico struct {
Nome string
Auditoria
}
func main() {
servico := Servico{Nome: "API"}
servico.Registrar()
}
Atividade 43
Crie duas structs embutidas em outra, ambas com o método Status(). Resolva a ambiguidade chamando os métodos pelos nomes dos campos embutidos.
Resultado esperado: As duas mensagens devem ser exibidas, sem tentar chamar diretamente servidor.Status().
Clique para ver como resolver!
package main
import "fmt"
type Rede struct{}
type Disco struct{}
func (Rede) Status() { fmt.Println("Rede OK") }
func (Disco) Status() { fmt.Println("Disco OK") }
type Servidor struct {
Rede
Disco
}
func main() {
servidor := Servidor{}
servidor.Rede.Status()
servidor.Disco.Status()
}
Atividade 44
Crie uma função dobrar que receba *int, use o operador * para alterar o valor apontado e invoque-a passando o endereço de uma variável com &.
Resultado esperado: Um valor iniciado em 21 deve passar a valer 42.
Clique para ver como resolver!
package main
import "fmt"
func dobrar(numero *int) {
*numero *= 2
}
func main() {
valor := 21
dobrar(&valor)
fmt.Println(valor)
}
Atividade 45
Crie ativar(s *Servidor) error, que altere o campo Ativo e devolva erro caso o ponteiro recebido seja nil.
Resultado esperado: Com um ponteiro válido, o servidor deve ficar ativo. Com nil, deve retornar erro em vez de acessar memória inválida.
Clique para ver como resolver!
package main
import "fmt"
type Servidor struct{ Ativo bool }
func ativar(s *Servidor) error {
if s == nil {
return fmt.Errorf("servidor não informado")
}
s.Ativo = true
return nil
}
func main() {
servidor := &Servidor{}
fmt.Println("Erro:", ativar(servidor), "Ativo:", servidor.Ativo)
fmt.Println("Ponteiro nil:", ativar(nil))
}
Tema 10 — Packages, módulos e dependências
Atividade 46
Crie o módulo exemplo.com/atividade46 com go mod init exemplo.com/atividade46. Dentro dele, crie main.go e execute o programa.
Resultado esperado: Após go run ., a aplicação deve mostrar Módulo funcionando. O diretório deve conter o arquivo go.mod.
Clique para ver como resolver!
// arquivo: main.go
package main
import "fmt"
func main() {
fmt.Println("Módulo funcionando")
}
Atividade 47
Crie um package próprio chamado calculadora, separado de main. Nele, exporte a função Somar e importe-a pelo caminho do módulo.
Resultado esperado: Ao executar go run . na raiz, a soma de 20 e 22 deve mostrar 42. Inicie o módulo com go mod init exemplo.com/atividade47.
Clique para ver como resolver!
// arquivo: calculadora/calculadora.go
package calculadora
func Somar(a, b int) int {
return a + b
}
// arquivo: main.go
package main
import (
"fmt"
"exemplo.com/atividade47/calculadora"
)
func main() {
fmt.Println(calculadora.Somar(20, 22))
}
Atividade 48
Crie um package saudacao com uma função exportada Ola e uma função auxiliar não exportada montarMensagem. Use apenas Ola a partir do main.
Resultado esperado: A aplicação deve imprimir Olá, Ana!. A função auxiliar com inicial minúscula deve ser utilizada apenas dentro de seu próprio package. Inicie o módulo com go mod init exemplo.com/atividade48.
Clique para ver como resolver!
// arquivo: saudacao/saudacao.go
package saudacao
func montarMensagem(nome string) string {
return "Olá, " + nome + "!"
}
func Ola(nome string) string {
return montarMensagem(nome)
}
// arquivo: main.go
package main
import (
"fmt"
"exemplo.com/atividade48/saudacao"
)
func main() {
fmt.Println(saudacao.Ola("Ana"))
}
Atividade 49
Organize uma aplicação com dois arquivos Go no mesmo diretório e no mesmo package main: um deve conter a função main, e o outro a função dobro.
Resultado esperado: Executando go run . (e não apenas go run main.go), o resultado deve ser 42. Inicie o módulo com go mod init exemplo.com/atividade49.
Clique para ver como resolver!
// arquivo: calculo.go
package main
func dobro(valor int) int {
return valor * 2
}
// arquivo: main.go
package main
import "fmt"
func main() {
fmt.Println(dobro(21))
}
Atividade 50
Crie um módulo e utilize uma biblioteca externa: o package github.com/google/uuid para gerar um identificador. Antes de executar, adicione a dependência com go get github.com/google/uuid e organize o go.mod com go mod tidy.
Resultado esperado: A execução deve mostrar um UUID válido. O go.mod deve registrar a dependência utilizada. É necessário acesso à internet para instalar o package.
Clique para ver como resolver!
// arquivo: main.go
package main
import (
"fmt"
"github.com/google/uuid"
)
func main() {
identificador := uuid.New()
fmt.Println(identificador.String())
}
Tema 11 — Entrada de dados, argumentos e interfaces de I/O
Atividade 51
Leia do terminal um nome sem espaços com fmt.Scan e mostre uma saudação.
Resultado esperado: Ao informar Ana, a saída deve ser Olá, Ana. Se a leitura falhar, deve aparecer uma mensagem de erro.
Clique para ver como resolver!
package main
import "fmt"
func main() {
var nome string
fmt.Print("Nome: ")
if _, err := fmt.Scan(&nome); err != nil {
fmt.Println("Erro de leitura:", err)
return
}
fmt.Println("Olá,", nome)
}
Atividade 52
Leia um nome completo usando bufio.Reader e remova espaços nas extremidades com strings.TrimSpace.
Resultado esperado: Informando Ana Maria seguido de Enter, a mensagem deve conter o nome completo, sem a quebra de linha.
Clique para ver como resolver!
package main
import (
"bufio"
"fmt"
"os"
"strings"
)
func main() {
leitor := bufio.NewReader(os.Stdin)
fmt.Print("Nome completo: ")
nome, err := leitor.ReadString('\n')
if err != nil {
fmt.Println("Erro de leitura:", err)
return
}
fmt.Println("Olá,", strings.TrimSpace(nome))
}
Atividade 53
Use bufio.Scanner para ler linhas do terminal. Encerre quando o usuário digitar sair e conte quantas linhas anteriores foram recebidas.
Resultado esperado: Com as entradas a, b e sair, o programa deve exibir 2 linhas.
Clique para ver como resolver!
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
total := 0
for scanner.Scan() {
if scanner.Text() == "sair" {
break
}
total++
}
if err := scanner.Err(); err != nil {
fmt.Fprintln(os.Stderr, "Erro de leitura:", err)
return
}
fmt.Printf("%d linhas\n", total)
}
Atividade 54
Faça um programa que receba um número pelos argumentos da linha de comando e o converta com strconv.Atoi. Valide tanto a falta do argumento quanto valores inválidos.
Resultado esperado: Executando go run . 21, a saída deve ser 42. Sem argumento ou com texto não numérico, a mensagem deve ir para stderr e o processo deve terminar com código diferente de zero.
Clique para ver como resolver!
package main
import (
"fmt"
"os"
"strconv"
)
func main() {
if len(os.Args) != 2 {
fmt.Fprintln(os.Stderr, "Uso: programa <numero>")
os.Exit(1)
}
numero, err := strconv.Atoi(os.Args[1])
if err != nil {
fmt.Fprintln(os.Stderr, "Número inválido:", err)
os.Exit(1)
}
fmt.Println(numero * 2)
}
Atividade 55
Crie uma função copiar que aceite io.Reader e io.Writer. Passe um strings.Reader como origem e os.Stdout como destino.
Resultado esperado: O programa deve imprimir Conteúdo copiado usando io.Copy, sem depender de um tipo específico de origem.
Clique para ver como resolver!
package main
import (
"fmt"
"io"
"os"
"strings"
)
func copiar(destino io.Writer, origem io.Reader) error {
_, err := io.Copy(destino, origem)
return err
}
func main() {
origem := strings.NewReader("Conteúdo copiado\n")
if err := copiar(os.Stdout, origem); err != nil {
fmt.Fprintln(os.Stderr, "Erro:", err)
}
}
Tema 12 — Criação, leitura e tratamento de arquivos
Atividade 56
Grave o texto Serviço iniciado em um arquivo chamado atividade.txt usando os.WriteFile. Em seguida, leia o arquivo com os.ReadFile.
Resultado esperado: O arquivo deve ser criado no diretório de execução, e o conteúdo lido deve ser idêntico ao texto gravado.
Clique para ver como resolver!
package main
import (
"fmt"
"os"
)
func main() {
err := os.WriteFile("atividade.txt", []byte("Serviço iniciado\n"), 0600)
if err != nil {
fmt.Println("Erro ao gravar:", err)
return
}
dados, err := os.ReadFile("atividade.txt")
if err != nil {
fmt.Println("Erro ao ler:", err)
return
}
fmt.Print(string(dados))
}
Atividade 57
Crie relatorio.txt com os.Create, grave duas linhas usando WriteString e garanta o fechamento do arquivo com defer.
Resultado esperado: O arquivo deve conter Linha 1 e Linha 2 em linhas separadas. Se a escrita falhar, o erro deve ser informado.
Clique para ver como resolver!
package main
import (
"fmt"
"os"
)
func escrever() error {
arquivo, err := os.Create("relatorio.txt")
if err != nil {
return err
}
defer arquivo.Close()
if _, err := arquivo.WriteString("Linha 1\n"); err != nil {
return err
}
if _, err := arquivo.WriteString("Linha 2\n"); err != nil {
return err
}
return nil
}
func main() {
if err := escrever(); err != nil {
fmt.Println("Erro:", err)
}
}
Atividade 58
Abra eventos.log usando os.OpenFile em modo de acréscimo (append) e grave uma linha por execução, sem sobrescrever as anteriores.
Resultado esperado: Ao executar o programa duas vezes, o arquivo deve conter duas linhas Evento registrado.
Clique para ver como resolver!
package main
import (
"fmt"
"os"
)
func registrar() error {
arquivo, err := os.OpenFile("eventos.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
if err != nil {
return err
}
defer arquivo.Close()
_, err = arquivo.WriteString("Evento registrado\n")
return err
}
func main() {
if err := registrar(); err != nil {
fmt.Println("Erro:", err)
}
}
Atividade 59
Leia eventos.log linha por linha com bufio.Scanner e conte quantas linhas existem. Crie o arquivo no exercício anterior antes de executar este.
Resultado esperado: Se o arquivo tiver três linhas, a saída deve ser Total: 3. Erros de abertura e leitura devem ser tratados.
Clique para ver como resolver!
package main
import (
"bufio"
"fmt"
"os"
)
func contarLinhas(caminho string) (int, error) {
arquivo, err := os.Open(caminho)
if err != nil {
return 0, err
}
defer arquivo.Close()
scanner := bufio.NewScanner(arquivo)
total := 0
for scanner.Scan() {
total++
}
return total, scanner.Err()
}
func main() {
total, err := contarLinhas("eventos.log")
if err != nil {
fmt.Println("Erro:", err)
return
}
fmt.Println("Total:", total)
}
Atividade 60
Crie uma função que verifique se um arquivo existe usando os.Stat, distinguindo um arquivo inexistente de outros erros.
Resultado esperado: Para um arquivo inexistente, a mensagem deve ser Arquivo não encontrado, e não um panic.
Clique para ver como resolver!
package main
import (
"fmt"
"os"
)
func verificar(caminho string) {
_, err := os.Stat(caminho)
if err == nil {
fmt.Println("Arquivo existe")
return
}
if os.IsNotExist(err) {
fmt.Println("Arquivo não encontrado")
return
}
fmt.Println("Erro ao verificar:", err)
}
func main() {
verificar("arquivo-que-nao-existe.txt")
}
Tema 13 — JSON e serialização de dados
Atividade 61
Crie uma struct Usuario com tags JSON para nome e idade. Converta a struct em JSON com json.Marshal e mostre o resultado.
Resultado esperado: O JSON deve ter as chaves nome e idade em minúsculas, com os valores Ana e 25.
Clique para ver como resolver!
package main
import (
"encoding/json"
"fmt"
)
type Usuario struct {
Nome string `json:"nome"`
Idade int `json:"idade"`
}
func main() {
usuario := Usuario{Nome: "Ana", Idade: 25}
dados, err := json.Marshal(usuario)
if err != nil {
fmt.Println("Erro:", err)
return
}
fmt.Println(string(dados))
}
Atividade 62
Receba um JSON literal que contenha nome e ativo e converta-o para uma struct usando json.Unmarshal.
Resultado esperado: A aplicação deve imprimir srv01 true.
Clique para ver como resolver!
package main
import (
"encoding/json"
"fmt"
)
type Servidor struct {
Nome string `json:"nome"`
Ativo bool `json:"ativo"`
}
func main() {
entrada := []byte(`{"nome":"srv01","ativo":true}`)
var servidor Servidor
if err := json.Unmarshal(entrada, &servidor); err != nil {
fmt.Println("Erro:", err)
return
}
fmt.Println(servidor.Nome, servidor.Ativo)
}
Atividade 63
Converta um slice de structs Servico para JSON usando json.MarshalIndent, para obter uma saída legível.
Resultado esperado: A saída deve ser um array JSON contendo os serviços ssh na porta 22 e https na porta 443.
Clique para ver como resolver!
package main
import (
"encoding/json"
"fmt"
)
type Servico struct {
Nome string `json:"nome"`
Porta int `json:"porta"`
}
func main() {
servicos := []Servico{{"ssh", 22}, {"https", 443}}
dados, err := json.MarshalIndent(servicos, "", " ")
if err != nil {
fmt.Println("Erro:", err)
return
}
fmt.Println(string(dados))
}
Atividade 64
Tente converter o texto JSON inválido {"idade":} para uma struct e trate o erro retornado por json.Unmarshal.
Resultado esperado: O programa deve informar que a conversão falhou, mas continuar sob controle, sem panic.
Clique para ver como resolver!
package main
import (
"encoding/json"
"fmt"
)
type Pessoa struct {
Idade int `json:"idade"`
}
func main() {
var pessoa Pessoa
err := json.Unmarshal([]byte(`{"idade":}`), &pessoa)
if err != nil {
fmt.Println("JSON inválido:", err)
return
}
fmt.Println(pessoa)
}
Atividade 65
Integre JSON e arquivos: grave uma struct Configuracao em config.json, leia o arquivo e converta o conteúdo de volta para uma struct.
Resultado esperado: Depois da leitura, devem aparecer porta=8080 e debug=true. Trate os erros de escrita, leitura e conversão.
Clique para ver como resolver!
package main
import (
"encoding/json"
"fmt"
"os"
)
type Configuracao struct {
Porta int `json:"porta"`
Debug bool `json:"debug"`
}
func main() {
original := Configuracao{Porta: 8080, Debug: true}
dados, err := json.MarshalIndent(original, "", " ")
if err != nil {
fmt.Println("Erro ao converter:", err)
return
}
if err := os.WriteFile("config.json", dados, 0600); err != nil {
fmt.Println("Erro ao gravar:", err)
return
}
conteudo, err := os.ReadFile("config.json")
if err != nil {
fmt.Println("Erro ao ler:", err)
return
}
var carregada Configuracao
if err := json.Unmarshal(conteudo, &carregada); err != nil {
fmt.Println("Erro no JSON:", err)
return
}
fmt.Printf("porta=%d debug=%t\n", carregada.Porta, carregada.Debug)
}
Tema 14 — Goroutines, WaitGroup e channels
Atividade 66
Execute duas funções em goroutines, cada uma mostrando uma mensagem, e use sync.WaitGroup para esperar as duas terminarem.
Resultado esperado: As duas mensagens devem aparecer antes de Concluído. A ordem entre as mensagens das goroutines pode variar.
Clique para ver como resolver!
package main
import (
"fmt"
"sync"
)
func executar(nome string, wg *sync.WaitGroup) {
defer wg.Done()
fmt.Println("Executando:", nome)
}
func main() {
var wg sync.WaitGroup
for _, nome := range []string{"A", "B"} {
wg.Add(1)
go executar(nome, &wg)
}
wg.Wait()
fmt.Println("Concluído")
}
Atividade 67
Inicie cinco goroutines em um for. Passe o número de cada tarefa como argumento da função anônima e espere todas terminarem.
Resultado esperado: Cada tarefa de 1 a 5 deve ser mostrada exatamente uma vez. A ordem não precisa ser crescente.
Clique para ver como resolver!
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
for i := 1; i <= 5; i++ {
wg.Add(1)
go func(numero int) {
defer wg.Done()
fmt.Println("Tarefa:", numero)
}(i)
}
wg.Wait()
}
Atividade 68
Crie um channel sem buffer de inteiros. Envie 42 por uma goroutine e receba esse valor em main.
Resultado esperado: A saída deve ser Recebido: 42. O envio e o recebimento devem se sincronizar pelo channel.
Clique para ver como resolver!
package main
import "fmt"
func main() {
numeros := make(chan int)
go func() {
numeros <- 42
}()
fmt.Println("Recebido:", <-numeros)
}
Atividade 69
Crie um channel de strings com buffer para duas mensagens. Envie dois valores antes de começar a receber e verifique len e cap.
Resultado esperado: Antes das leituras, len e cap devem ser 2. As mensagens devem sair na ordem em que entraram.
Clique para ver como resolver!
package main
import "fmt"
func main() {
mensagens := make(chan string, 2)
mensagens <- "primeira"
mensagens <- "segunda"
fmt.Println("len:", len(mensagens), "cap:", cap(mensagens))
fmt.Println(<-mensagens)
fmt.Println(<-mensagens)
}
Atividade 70
Crie uma goroutine que envie os números de 1 a 5 e feche o channel. Em main, percorra-o com range e calcule a soma. Depois, faça uma leitura com valor, ok.
Resultado esperado: A soma deve ser 15; após o fechamento e esvaziamento do channel, ok deve ser false.
Clique para ver como resolver!
package main
import "fmt"
func main() {
numeros := make(chan int)
go func() {
defer close(numeros)
for i := 1; i <= 5; i++ {
numeros <- i
}
}()
total := 0
for numero := range numeros {
total += numero
}
valor, ok := <-numeros
fmt.Println("Soma:", total)
fmt.Println("Depois de fechar:", valor, ok)
}
Tema 15 — Select e context
Atividade 71
Crie dois channels de strings com buffer. Envie uma mensagem para cada um e use select para receber uma delas.
Resultado esperado: Uma das duas mensagens deve aparecer. Se mais de um caso estiver pronto, não há garantia de qual será escolhido.
Clique para ver como resolver!
package main
import "fmt"
func main() {
primeiro := make(chan string, 1)
segundo := make(chan string, 1)
primeiro <- "mensagem A"
segundo <- "mensagem B"
select {
case texto := <-primeiro:
fmt.Println(texto)
case texto := <-segundo:
fmt.Println(texto)
}
}
Atividade 72
Faça um select que tente receber de um channel vazio e tenha um caso default para não bloquear.
Resultado esperado: A mensagem Nenhum dado disponível deve aparecer imediatamente.
Clique para ver como resolver!
package main
import "fmt"
func main() {
mensagens := make(chan string)
select {
case mensagem := <-mensagens:
fmt.Println(mensagem)
default:
fmt.Println("Nenhum dado disponível")
}
}
Atividade 73
Crie um context.WithCancel e uma goroutine que espere seu cancelamento. Cancele no main e sincronize o encerramento com WaitGroup.
Resultado esperado: A goroutine deve reconhecer o cancelamento e imprimir Encerrando, e o programa só deve terminar depois disso.
Clique para ver como resolver!
package main
import (
"context"
"fmt"
"sync"
)
func main() {
ctx, cancelar := context.WithCancel(context.Background())
defer cancelar()
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
<-ctx.Done()
fmt.Println("Encerrando:", ctx.Err())
}()
cancelar()
wg.Wait()
}
Atividade 74
Crie um context.WithTimeout de 100ms e simule um serviço que só termina após 300ms usando select com time.After.
Resultado esperado: O timeout deve vencer primeiro; a mensagem deve indicar context deadline exceeded.
Clique para ver como resolver!
package main
import (
"context"
"fmt"
"time"
)
func main() {
ctx, cancelar := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer cancelar()
select {
case <-time.After(300 * time.Millisecond):
fmt.Println("Serviço terminou")
case <-ctx.Done():
fmt.Println("Operação cancelada:", ctx.Err())
}
}
Atividade 75
Escreva a função executar(ctx context.Context) que aguarde uma resposta por time.After, mas respeite cancelamento. Chame-a uma vez com um contexto cancelado e outra com prazo vencendo antes da resposta.
Resultado esperado: A primeira chamada deve informar context canceled e a segunda context deadline exceeded, sem esperar a operação inteira.
Clique para ver como resolver!
package main
import (
"context"
"fmt"
"time"
)
func executar(ctx context.Context) error {
select {
case <-time.After(200 * time.Millisecond):
return nil
case <-ctx.Done():
return ctx.Err()
}
}
func main() {
cancelado, cancelar := context.WithCancel(context.Background())
cancelar()
fmt.Println("Cancelado:", executar(cancelado))
limitado, parar := context.WithTimeout(context.Background(), 50*time.Millisecond)
defer parar()
fmt.Println("Timeout:", executar(limitado))
}
Tema 16 — Mutex, atomic, fan-out e fan-in
Atividade 76
Inicie 100 goroutines que incrementem uma variável compartilhada, protegendo a operação com sync.Mutex e usando WaitGroup.
Resultado esperado: O contador final deve ser sempre 100, sem depender da ordem de execução das goroutines.
Clique para ver como resolver!
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
var mutex sync.Mutex
contador := 0
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
defer wg.Done()
mutex.Lock()
contador++
mutex.Unlock()
}()
}
wg.Wait()
fmt.Println("Contador:", contador)
}
Atividade 77
Faça o mesmo contador do exercício anterior usando atomic.Int64 em vez de sync.Mutex.
Resultado esperado: Com 100 goroutines, o resultado deve ser sempre 100. Consulte o valor final usando Load().
Clique para ver como resolver!
package main
import (
"fmt"
"sync"
"sync/atomic"
)
func main() {
var wg sync.WaitGroup
var contador atomic.Int64
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
defer wg.Done()
contador.Add(1)
}()
}
wg.Wait()
fmt.Println("Contador:", contador.Load())
}
Atividade 78
Implemente um fan-out com dois workers lendo tarefas do mesmo channel. Cada worker deve calcular o dobro do número e enviar o resultado para outro channel.
Resultado esperado: As tarefas 1 a 5 devem produzir os resultados 2, 4, 6, 8 e 10, cada um uma única vez. A ordem dos resultados pode variar.
Clique para ver como resolver!
package main
import (
"fmt"
"sync"
)
func worker(tarefas <-chan int, resultados chan<- int, wg *sync.WaitGroup) {
defer wg.Done()
for tarefa := range tarefas {
resultados <- tarefa * 2
}
}
func main() {
tarefas := make(chan int)
resultados := make(chan int)
var wg sync.WaitGroup
for i := 0; i < 2; i++ {
wg.Add(1)
go worker(tarefas, resultados, &wg)
}
go func() {
defer close(tarefas)
for i := 1; i <= 5; i++ {
tarefas <- i
}
}()
go func() {
wg.Wait()
close(resultados)
}()
total := 0
for resultado := range resultados {
fmt.Println("Resultado:", resultado)
total += resultado
}
fmt.Println("Soma:", total)
}
Atividade 79
Implemente um fan-in que reúna os valores de dois channels em um único channel de saída. Use WaitGroup para fechar a saída apenas quando as duas origens terminarem.
Resultado esperado: Os valores 1, 2, 10 e 20 devem ser recebidos uma vez cada; a soma final deve ser 33.
Clique para ver como resolver!
package main
import (
"fmt"
"sync"
)
func produzir(valores ...int) <-chan int {
saida := make(chan int)
go func() {
defer close(saida)
for _, valor := range valores {
saida <- valor
}
}()
return saida
}
func juntar(a, b <-chan int) <-chan int {
saida := make(chan int)
var wg sync.WaitGroup
repassar := func(entrada <-chan int) {
defer wg.Done()
for valor := range entrada {
saida <- valor
}
}
wg.Add(2)
go repassar(a)
go repassar(b)
go func() {
wg.Wait()
close(saida)
}()
return saida
}
func main() {
total := 0
for numero := range juntar(produzir(1, 2), produzir(10, 20)) {
fmt.Println("Recebido:", numero)
total += numero
}
fmt.Println("Soma:", total)
}
Atividade 80
Combine goroutines, channels e Mutex: distribua 50 valores por um channel para três workers e faça cada worker incrementar, sob Mutex, um contador de tarefas processadas.
Resultado esperado: Todas as 50 tarefas devem ser processadas uma vez, e o contador final deve mostrar 50.
Clique para ver como resolver!
package main
import (
"fmt"
"sync"
)
func main() {
tarefas := make(chan int)
var wg sync.WaitGroup
var mutex sync.Mutex
processadas := 0
for i := 0; i < 3; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for range tarefas {
mutex.Lock()
processadas++
mutex.Unlock()
}
}()
}
for i := 1; i <= 50; i++ {
tarefas <- i
}
close(tarefas)
wg.Wait()
fmt.Println("Processadas:", processadas)
}
Tema 17 — Datas, durações, timers e tickers
Atividade 81
Consulte a data atual com time.Now() e mostre-a no formato brasileiro dd/mm/aaaa hh:mm:ss, usando o layout de referência do Go.
Resultado esperado: A saída deve seguir o formato 12/10/2026 14:30:00 (com os valores reais da execução).
Clique para ver como resolver!
package main
import (
"fmt"
"time"
)
func main() {
agora := time.Now()
fmt.Println(agora.Format("02/01/2006 15:04:05"))
}
Atividade 82
Registre o início de uma operação, pause a goroutine atual por 100ms com time.Sleep e calcule o tempo decorrido usando time.Since.
Resultado esperado: O tempo medido deve ser de aproximadamente 100ms ou um pouco mais.
Clique para ver como resolver!
package main
import (
"fmt"
"time"
)
func main() {
inicio := time.Now()
time.Sleep(100 * time.Millisecond)
fmt.Println("Decorrido:", time.Since(inicio))
}
Atividade 83
Crie um time.Timer para disparar após 150ms. Aguarde pelo channel timer.C antes de mostrar a mensagem de conclusão.
Resultado esperado: A mensagem Timer disparou só deve aparecer após aproximadamente 150ms.
Clique para ver como resolver!
package main
import (
"fmt"
"time"
)
func main() {
timer := time.NewTimer(150 * time.Millisecond)
defer timer.Stop()
<-timer.C
fmt.Println("Timer disparou")
}
Atividade 84
Crie um time.Timer configurado para 500ms, interrompa-o antes do disparo usando Stop() e reinicie-o para 100ms usando Reset().
Resultado esperado: O programa deve mostrar que interrompeu o timer anterior e deve receber o novo disparo após aproximadamente 100ms.
Clique para ver como resolver!
package main
import (
"fmt"
"time"
)
func main() {
timer := time.NewTimer(500 * time.Millisecond)
defer timer.Stop()
interrompido := timer.Stop()
fmt.Println("Timer interrompido:", interrompido)
timer.Reset(100 * time.Millisecond)
<-timer.C
fmt.Println("Timer reiniciado disparou")
}
Atividade 85
Use time.NewTicker para executar uma ação periodicamente a cada 100ms e encerre após três disparos. Garanta que o ticker seja parado.
Resultado esperado: A saída deve mostrar Tick 1, Tick 2 e Tick 3, e a aplicação deve terminar.
Clique para ver como resolver!
package main
import (
"fmt"
"time"
)
func main() {
ticker := time.NewTicker(100 * time.Millisecond)
defer ticker.Stop()
for i := 1; i <= 3; i++ {
<-ticker.C
fmt.Println("Tick", i)
}
}
Tema 18 — Logs, panic e recover
Atividade 86
Use o package log para registrar uma mensagem de inicialização e outra informando a conclusão de uma operação.
Resultado esperado: As duas mensagens devem aparecer com o formato padrão do package log, incluindo data e horário.
Clique para ver como resolver!
package main
import "log"
func main() {
log.Println("Aplicação iniciada")
log.Println("Operação concluída")
}
Atividade 87
Configure log/slog com um handler de texto e registre mensagens dos níveis Info, Warn e Error, com atributos estruturados.
Resultado esperado: Devem aparecer três registros com níveis diferentes e seus respectivos atributos.
Clique para ver como resolver!
package main
import (
"log/slog"
"os"
)
func main() {
logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
logger.Info("Servidor iniciado", "porta", 8080)
logger.Warn("Consumo elevado", "memoria_mb", 900)
logger.Error("Serviço indisponível", "servico", "banco")
}
Atividade 88
Escreva uma função validarPorta que retorne error quando a porta estiver fora do intervalo de 1 a 65535. Em main, registre a falha com log.Println, sem usar panic.
Resultado esperado: Para a porta 70000, deve ser registrado um erro recuperável e o programa deve continuar até imprimir Verificação concluída.
Clique para ver como resolver!
package main
import (
"fmt"
"log"
)
func validarPorta(porta int) error {
if porta < 1 || porta > 65535 {
return fmt.Errorf("porta inválida: %d", porta)
}
return nil
}
func main() {
if err := validarPorta(70000); err != nil {
log.Println("Falha:", err)
}
fmt.Println("Verificação concluída")
}
Atividade 89
Crie uma função que provoque propositalmente panic e recupere o controle usando defer e recover dentro dessa própria função.
Resultado esperado: A mensagem do panic deve ser capturada; após a chamada, o main deve conseguir mostrar Programa continua.
Clique para ver como resolver!
package main
import "fmt"
func executar() {
defer func() {
if valor := recover(); valor != nil {
fmt.Println("Recuperado:", valor)
}
}()
panic("falha simulada")
}
func main() {
executar()
fmt.Println("Programa continua")
}
Atividade 90
Integre validação e log estruturado: processe três códigos de status (200, 404, 500) e gere Info, Warn ou Error conforme o valor, usando slog.
Resultado esperado: Devem aparecer três registros, um em cada nível, com os códigos corretos. Não use panic para representar esses estados.
Clique para ver como resolver!
package main
import (
"log/slog"
"os"
)
func main() {
logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
for _, codigo := range []int{200, 404, 500} {
switch {
case codigo >= 500:
logger.Error("Falha no servidor", "status", codigo)
case codigo >= 400:
logger.Warn("Recurso indisponível", "status", codigo)
default:
logger.Info("Requisição concluída", "status", codigo)
}
}
}
Tema 19 — Testes, compilação e revisão final
Atividade 91
Escreva uma função Dobrar e um teste automatizado básico em main_test.go, usando o package testing.
Resultado esperado: Com go test, o teste deve passar ao verificar que Dobrar(21) é 42. Crie o módulo com go mod init exemplo.com/atividade91.
Clique para ver como resolver!
// arquivo: main.go
package main
func Dobrar(valor int) int {
return valor * 2
}
// arquivo: main_test.go
package main
import "testing"
func TestDobrar(t *testing.T) {
resultado := Dobrar(21)
if resultado != 42 {
t.Errorf("Esperado 42, recebeu %d", resultado)
}
}
Atividade 92
Crie uma função Par e testes de tabela cobrindo números positivos, negativos e zero. Use t.Run para cada caso.
Resultado esperado: Executando go test -v, os casos 2, 3, 0 e -4 devem passar. Crie o módulo com go mod init exemplo.com/atividade92.
Clique para ver como resolver!
// arquivo: par.go
package main
func Par(numero int) bool {
return numero%2 == 0
}
// arquivo: par_test.go
package main
import "testing"
func TestPar(t *testing.T) {
casos := []struct {
nome string
numero int
esperado bool
}{
{"positivo par", 2, true},
{"positivo ímpar", 3, false},
{"zero", 0, true},
{"negativo par", -4, true},
}
for _, caso := range casos {
t.Run(caso.nome, func(t *testing.T) {
obtido := Par(caso.numero)
if obtido != caso.esperado {
t.Errorf("Para %d, esperado %t, obtido %t", caso.numero, caso.esperado, obtido)
}
})
}
}
Atividade 93
Escreva Dividir(a, b int) (int, error) e faça um teste de tabela que cubra divisão válida e divisão por zero.
Resultado esperado: Todos os testes devem passar, inclusive o caso que espera erro ao dividir por zero. Crie o módulo com go mod init exemplo.com/atividade93.
Clique para ver como resolver!
// arquivo: divisao.go
package main
import "fmt"
func Dividir(a, b int) (int, error) {
if b == 0 {
return 0, fmt.Errorf("divisor não pode ser zero")
}
return a / b, nil
}
// arquivo: divisao_test.go
package main
import "testing"
func TestDividir(t *testing.T) {
casos := []struct {
a, b int
esperado int
deveDarErro bool
}{
{10, 2, 5, false},
{9, 3, 3, false},
{10, 0, 0, true},
}
for _, caso := range casos {
obtido, err := Dividir(caso.a, caso.b)
if (err != nil) != caso.deveDarErro {
t.Errorf("Dividir(%d, %d): erro inesperado: %v", caso.a, caso.b, err)
continue
}
if err == nil && obtido != caso.esperado {
t.Errorf("Esperado %d, obtido %d", caso.esperado, obtido)
}
}
}
Atividade 94
Crie um programa simples que mostre Sistema pronto e gere binários para Linux amd64, Windows amd64 e macOS arm64 usando go build e as variáveis GOOS/GOARCH.
Resultado esperado: Devem ser gerados três executáveis com nomes distintos; o binário Windows deve terminar em .exe. Execute os comandos indicados nos comentários.
Clique para ver como resolver!
// arquivo: main.go
// Comandos no terminal (na raiz do módulo):
// go mod init exemplo.com/atividade94
// GOOS=linux GOARCH=amd64 go build -o app-linux .
// GOOS=windows GOARCH=amd64 go build -o app-windows.exe .
// GOOS=darwin GOARCH=arm64 go build -o app-macos .
package main
import "fmt"
func main() {
fmt.Println("Sistema pronto")
}
Atividade 95
Desafio integrador: crie uma struct Servico, converta um slice de serviços para JSON, grave servicos.json, leia o arquivo novamente e conte quantos serviços estão ativos. Separe a contagem em uma função que possa ser testada.
Resultado esperado: O programa deve mostrar Ativos: 2. O arquivo precisa conter JSON válido, e go test deve aprovar um teste de tabela com casos de zero, um e dois serviços ativos. Crie o módulo com go mod init exemplo.com/atividade95.
Clique para ver como resolver!
// arquivo: servicos.go
package main
type Servico struct {
Nome string `json:"nome"`
Ativo bool `json:"ativo"`
}
func ContarAtivos(servicos []Servico) int {
total := 0
for _, servico := range servicos {
if servico.Ativo {
total++
}
}
return total
}
// arquivo: main.go
package main
import (
"encoding/json"
"fmt"
"os"
)
func main() {
servicos := []Servico{
{Nome: "ssh", Ativo: true},
{Nome: "http", Ativo: false},
{Nome: "dns", Ativo: true},
}
dados, err := json.MarshalIndent(servicos, "", " ")
if err != nil {
fmt.Println("Erro no JSON:", err)
return
}
if err := os.WriteFile("servicos.json", dados, 0600); err != nil {
fmt.Println("Erro ao gravar:", err)
return
}
lidos, err := os.ReadFile("servicos.json")
if err != nil {
fmt.Println("Erro ao ler:", err)
return
}
var recuperados []Servico
if err := json.Unmarshal(lidos, &recuperados); err != nil {
fmt.Println("Erro ao interpretar JSON:", err)
return
}
fmt.Println("Ativos:", ContarAtivos(recuperados))
}
// arquivo: servicos_test.go
package main
import "testing"
func TestContarAtivos(t *testing.T) {
casos := []struct {
nome string
entrada []Servico
esperado int
}{
{"nenhum", nil, 0},
{"um", []Servico{{"ssh", true}, {"http", false}}, 1},
{"dois", []Servico{{"ssh", true}, {"http", true}}, 2},
}
for _, caso := range casos {
t.Run(caso.nome, func(t *testing.T) {
resultado := ContarAtivos(caso.entrada)
if resultado != caso.esperado {
t.Errorf("Esperado %d, recebido %d", caso.esperado, resultado)
}
})
}
}