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 construir um sistema de gerenciamento de inventário que utiliza erros sentinela para comunicar condições de falha específicas. Você definirá variáveis de erro no nível do pacote que os chamadores podem verificar para lidar com 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 de falha comuns.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 contém 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 de compra da entrada. Crie um inventário, adicione o item
"laptop"com a quantidade5, e então tente 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 para comprar
- Linha 2: Quantidade para comprar
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 for bem-sucedido: imprima
Successfully purchased [quantity] units of '[name]'
Por exemplo, dados laptop e 3, sua saída deve ser:
Successfully purchased 3 units of 'laptop'E dados phone e 2, sua saída deve ser:
Error: Item 'phone' does not exist in our inventoryE dados laptop e 10, sua saída deve ser:
Error: Cannot purchase 10 units of 'laptop' - not enough in stockFolha de consulta
Um erro sentinela é uma variável de erro predefinida em nível de pacote que representa uma condição de erro específica. Erros sentinela são declarados uma vez e reutilizados em todo o seu código.
Defina erros sentinela como variáveis em nível de pacote, normalmente 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 esses 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
}Quem chama a função pode 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, como io.EOF e sql.ErrNoRows.
Experimente 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 ExternosGo Workspace e MódulosPacotes e ImportaçõesNomes Exportados vs Não ExportadosIntrodução a OOP em GoStructs como ClassesDefinindo Métodos em StructsRecebedores de Ponteiro vs ValorInicialização de StructsFunções ConstrutorasRecapitulação - Calculadora Simples4Interfaces
Introdução a InterfacesImplementação ImplícitaInterface como ContratoInterface Vazia (any)Type AssertionType SwitchComposição de InterfacesInterfaces Stringer & ErrorRecapitulação - Calculadora de Formas7Encapsulamento
Campos Exportados vs Não ExportadosEncapsulamento em Nível de PacoteMétodos Getter e SetterOcultação de Informação em GoRecapitulação - Registros de Estudantes10Generics (Go 1.18+)
Introdução a GenericsParâmetros de TipoRestrições de TipoStructs GenéricasSolução Alternativa para Métodos GenéricosRecapitulação - Coleção Genérica2Mergulho Profundo em Tipos e Structs
Tipos Básicos e CompostosDefinições de Tipos PersonalizadosStruct TagsStructs AnônimasStructs AninhadasZero Values e PadrõesRecapitulação - Agenda de Contatos5Composição em vez de Herança
Por que Go não possui HerançaFundamentos de Incorporação de StructsPromoção de MétodosIncorporando Múltiplas StructsIncorporação vs AgregaçãoShadowing de Métodos IncorporadosRecapitulação - Hierarquia de Funcionários8Tratamento de Erros e OOP
A Interface errorTipos de Erro PersonalizadosError Wrapping (fmt.Errorf)Erros Sentinelaerrors.Is() e errors.As()Panic, Defer e RecoverRecapitulação - File Parser3Ponteiros e Memória
Fundamentos de Ponteiros em GoPonteiros para StructsPassagem por Valor vs ReferênciaA Função new()Garbage Collection em GoRecapitulação - Construtor de Lista Encadeada6Polimorfismo em Go
Polimorfismo via InterfacesDuck Typing em GoRegras de Satisfação de InterfaceColeções PolimórficasInjeção de DependênciaRecapitulação - Processador de Pagamentos9Concorrência e OOP
Fundamentos de GoroutinesCanais e ComunicaçãoCanais com Buffer vs. Sem BufferInstrução Selectsync.Mutex e sync.RWMutexsync.WaitGroupDesign de Structs Thread-SafeRecapitulação - Worker Pool