Erros Sentinela
Parte da seção Programação Orientada a Objetos do Journey de GO da Coddy. Lição 55 de 107.
Um erro sentinela é uma variável de erro predefinida em nível de pacote que representa uma condição de erro específica. Ao contrário de criar novos erros a cada vez com errors.New, os erros sentinela são declarados uma única vez e reutilizados em todo o seu código.
Você define erros sentinela como variáveis de nível de pacote, geralmente com nomes começando com Err:
package user
import "errors"
var (
ErrNotFound = errors.New("user not found")
ErrInvalidEmail = errors.New("invalid email address")
ErrDuplicate = errors.New("user already exists")
)As funções retornam estes erros predefinidos quando condições específicas ocorrem:
func FindUser(id int) (*User, error) {
user, exists := users[id]
if !exists {
return nil, ErrNotFound
}
return user, nil
}O poder dos erros sentinela é que os chamadores podem verificar condições de erro específicas comparando os valores de erro diretamente:
user, err := FindUser(42)
if err == ErrNotFound {
fmt.Println("Creating new user...")
} else if err != nil {
fmt.Println("Unexpected error:", err)
}A biblioteca padrão do Go usa erros sentinela extensivamente. Por exemplo, io.EOF sinaliza o fim da entrada, e sql.ErrNoRows indica um resultado de consulta vazio. Este padrão cria um contrato claro entre os pacotes e seus usuários sobre quais erros esperar.
No entanto, verificações de igualdade simples falham quando os erros são envolvidos. Na próxima lição, você aprenderá como errors.Is() resolve esse problema verificando toda a cadeia de erros.
Desafio
FácilVamos criar um sistema de gerenciamento de inventário que usa erros sentinela para comunicar condições específicas de falha. Você definirá variáveis de erro no nível do pacote que os chamadores poderão verificar para tratar diferentes cenários adequadamente.
Você organizará seu código em dois arquivos:
inventory.go: Crie um sistema de inventário com erros sentinela predefinidos para casos comuns de falha.Defina três erros sentinela no nível do pacote:
ErrItemNotFoundcom a mensagem"item not found in inventory"ErrOutOfStockcom a mensagem"item is out of stock"ErrInsufficientQuantitycom a mensagem"insufficient quantity available"
Crie uma struct
Inventoryque contenha um mapa de nomes de itens para suas quantidades. Implemente estes métodos:NewInventory() *Inventory- cria um inventário vazioAddItem(name string, quantity int)- adiciona ou atualiza a quantidade de um itemGetQuantity(name string) (int, error)- retorna a quantidade de um item ouErrItemNotFoundse ele não existirPurchase(name string, quantity int) error- tenta comprar itens. RetorneErrItemNotFoundse o item não existir,ErrOutOfStockse a quantidade atual for zero ouErrInsufficientQuantityse a quantidade solicitada exceder o estoque disponível. Em caso de sucesso, reduza o inventário e retornenil.
main.go: Leia as operações de inventário da entrada e demonstre como os chamadores podem verificar erros sentinela específicos usando comparação direta de igualdade.Leia o nome do item e a quantidade a ser comprada da entrada. Crie um inventário, adicione o item
"laptop"com a quantidade5e tente realizar a compra. Com base no erro retornado, imprima uma mensagem específica mostrando que você identificou qual erro sentinela ocorreu.
As seguintes entradas serão fornecidas:
- Linha 1: Nome do item a ser comprado
- Linha 2: Quantidade a ser comprada
Trate o resultado verificando qual erro específico foi retornado:
- Se
err == ErrItemNotFound: imprimaError: Item '[name]' does not exist in our inventory - Se
err == ErrOutOfStock: imprimaError: Item '[name]' is currently out of stock - Se
err == ErrInsufficientQuantity: imprimaError: Cannot purchase [quantity] units of '[name]' - not enough in stock - Se tiver sucesso: imprima
Successfully purchased [quantity] units of '[name]'
Por exemplo, dadas as entradas laptop e 3, sua saída deverá ser:
Successfully purchased 3 units of 'laptop'E, dadas as entradas phone e 2, sua saída deverá ser:
Error: Item 'phone' does not exist in our inventoryE, dadas as entradas laptop e 10, sua saída deverá ser:
Error: Cannot purchase 10 units of 'laptop' - not enough in stockExperimente você mesmo
package main
import (
"fmt"
)
func main() {
// Ler a entrada
var itemName string
var quantity int
fmt.Scanln(&itemName)
fmt.Scanln(&quantity)
// Criar inventário e adicionar "laptop" com quantidade 5
inv := NewInventory()
inv.AddItem("laptop", 5)
// TODO: Tentar comprar o item
// Chamar inv.Purchase(itemName, quantity)
// TODO: Verificar qual erro sentinela específico foi retornado usando comparação direta de igualdade
// Usar instruções if/else if para verificar:
// - err == ErrItemNotFound
// - err == ErrOutOfStock
// - err == ErrInsufficientQuantity
// - err == nil (sucesso)
// TODO: Imprimir a mensagem apropriada com base no erro
// Se ErrItemNotFound: fmt.Printf("Error: Item '%s' does not exist in our inventory\n", itemName)
// Se ErrOutOfStock: fmt.Printf("Error: Item '%s' is currently out of stock\n", itemName)
// Se ErrInsufficientQuantity: fmt.Printf("Error: Cannot purchase %d units of '%s' - not enough in stock\n", quantity, itemName)
// Se sucesso: fmt.Printf("Successfully purchased %d units of '%s'\n", quantity, itemName)
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Fundamentos de OOP em Go
Arquivos ExternosWorkspace e Módulos do GoPacotes e ImportsNomes Exportados vs. Não ExportadosIntrodução à OOP em GoStructs como ClassesDefinindo Métodos em StructsReceptores de Ponteiro vs. de ValorInicialização de StructsFunções ConstrutorasRecapitulação — Calculadora Simples4Interfaces
Introdução às InterfacesImplementação ImplícitaInterface como ContratoInterface Vazia (any)Asserção de TipoSwitch de TipoComposição de InterfacesInterfaces Stringer e ErrorRecapitulação - Calculadora de Formas7Encapsulamento
Campos Exportados vs. Não ExportadosEncapsulamento no Nível de PacoteMétodos Getters e SettersOcultação de Informações em GoRecapitulação - Registros de Alunos10Genéricos (Go 1.18+)
Introdução aos genéricosParâmetros de tipoRestrições de tipoStructs genéricasAlternativa para métodos genéricosRecapitulação - Coleção genérica13Padrões de Projeto — Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonPadrão FactoryPadrão Abstract FactoryPadrão ObserverPadrão Strategy2Tipos e Structs em Profundidade
Tipos Básicos e CompostosDefinições de Tipos PersonalizadosTags de StructStructs AnônimasStructs AninhadasValores Zero e PadrõesRecapitulação - Livro de Contatos5Composição em vez de herança
Por que Go não tem herançaNoções básicas de incorporação de structsPromoção de métodosIncorporação de várias structsIncorporação vs agregaçãoOcultação de métodos incorporadosRecapitulação - Hierarquia de funcionários8Tratamento de Erros e OOP
A interface errorTipos de Erro PersonalizadosEncapsulamento de Erros (fmt.Errorf)Erros Sentinelaerrors.Is() e errors.As()Panic, Defer e RecoverRecapitulação - Analisador de Arquivos3Ponteiros e Memória
Fundamentos de Ponteiros em GoPonteiros para StructsPassagem por Valor vs. ReferênciaA Função new()Coleta de Lixo em GoRecapitulação - Construtor de Lista Ligada6Polimorfismo em Go
Polimorfismo via InterfacesDuck Typing em GoRegras de Satisfação de InterfacesColeções PolimórficasInjeção de DependênciasRecapitulação - Processador de Pagamentos9Concorrência e OOP
Fundamentos de GoroutinesCanais e comunicaçãoCanais com e sem bufferInstrução selectsync.Mutex e sync.RWMutexsync.WaitGroupDesign de structs thread-safeRevisão - Worker PoolPratique por conta própria: Compilador de Go online