A Interface error
Parte da seção Programação Orientada a Objetos do Journey de GO da Coddy — lição 52 de 107.
Em Go, os erros são tratados por meio de uma interface integrada chamada error. Esta interface é notavelmente simples — ela requer apenas um método:
type error interface {
Error() string
}Qualquer tipo que implemente um método Error() que retorna uma string satisfaz automaticamente a interface error. Este é o sistema de interfaces do Go em funcionamento — nenhuma declaração explícita é necessária.
A maneira mais comum de criar erros é usando o pacote errors:
import "errors"
func Divide(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("cannot divide by zero")
}
return a / b, nil
}As funções em Go normalmente retornam um erro como seu último valor de retorno. Quando nenhum erro ocorre, você retorna nil. O chamador então verifica se o erro não é nulo antes de prosseguir:
result, err := Divide(10, 0)
if err != nil {
fmt.Println("Error:", err.Error())
return
}
fmt.Println("Result:", result)Você também pode usar fmt.Errorf para criar mensagens de erro formatadas:
func GetUser(id int) (string, error) {
if id <= 0 {
return "", fmt.Errorf("invalid user id: %d", id)
}
return "Alice", nil
}Este padrão de retornar erros em vez de lançar exceções é fundamental para o Go. Isso torna o tratamento de erros explícito e força você a considerar o que acontece quando as coisas dão errado — um aspecto fundamental para escrever código robusto e orientado a objetos.
Desafio
FácilVamos construir um sistema de conta bancária que demonstra o padrão de tratamento de erros do Go. Você criará uma conta simples que valida operações e retorna erros significativos quando algo dá errado.
Você organizará seu código em dois arquivos:
account.go: Crie uma structBankAccountcom um campo exportadoOwner(string) e um campo não exportadobalance(float64). Implemente o seguinte:NewBankAccount(owner string, initialDeposit float64) (*BankAccount, error)- um construtor que retorna um erro com a mensagem"initial deposit must be positive"se o depósito inicial for zero ou negativo. Caso contrário, crie e retorne a conta.Deposit(amount float64) error- adiciona dinheiro ao saldo. Retorne um erro com a mensagem"deposit amount must be positive"se o valor for zero ou negativo.Withdraw(amount float64) error- remove dinheiro do saldo. Retorne um erro com a mensagem"withdrawal amount must be positive"se o valor for zero ou negativo, ou"insufficient funds"se o saldo for menor que o valor do saque.Balance() float64- retorna o saldo atual
main.go: Leia os detalhes da conta e os valores das transações da entrada. Tente criar uma conta e realizar as operações. Após cada operação, imprima a mensagem de sucesso ou a mensagem de erro.
As seguintes entradas serão fornecidas:
- Linha 1: Nome do proprietário (Owner)
- Linha 2: Valor do depósito inicial
- Linha 3: Valor do depósito
- Linha 4: Valor do saque
Para cada operação, imprima o resultado:
- Após a criação da conta:
Account created for [Owner] with balance: $[balance]ouError: [error message] - Após o depósito:
Deposited successfully. New balance: $[balance]ouError: [error message] - Após o saque:
Withdrew successfully. New balance: $[balance]ouError: [error message]
Formate todos os valores de saldo com duas casas decimais. Se a criação da conta falhar, pule as operações restantes.
Por exemplo, dados Alice, 100, 50, e 200, sua saída deve ser:
Account created for Alice with balance: $100.00
Deposited successfully. New balance: $150.00
Error: insufficient fundsE dados Bob, -50, 25, e 10, sua saída deve ser:
Error: initial deposit must be positiveFolha de consulta
O Go lida com erros por meio da interface error integrada, que requer um único método:
type error interface {
Error() string
}Crie erros usando o pacote errors:
import "errors"
func Divide(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("cannot divide by zero")
}
return a / b, nil
}As funções retornam erros como o último valor de retorno. Retorne nil quando nenhum erro ocorrer:
result, err := Divide(10, 0)
if err != nil {
fmt.Println("Error:", err.Error())
return
}
fmt.Println("Result:", result)Use fmt.Errorf para mensagens de erro formatadas:
func GetUser(id int) (string, error) {
if id <= 0 {
return "", fmt.Errorf("invalid user id: %d", id)
}
return "Alice", nil
}Experimente você mesmo
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Ler o nome do proprietário
ownerName, _ := reader.ReadString('\n')
ownerName = strings.TrimSpace(ownerName)
// Ler o depósito inicial
initialDepositStr, _ := reader.ReadString('\n')
initialDeposit, _ := strconv.ParseFloat(strings.TrimSpace(initialDepositStr), 64)
// Ler o valor do depósito
depositStr, _ := reader.ReadString('\n')
depositAmount, _ := strconv.ParseFloat(strings.TrimSpace(depositStr), 64)
// Ler o valor do saque
withdrawStr, _ := reader.ReadString('\n')
withdrawAmount, _ := strconv.ParseFloat(strings.TrimSpace(withdrawStr), 64)
// TODO: Criar uma nova conta bancária usando NewBankAccount
// Se houver um erro, imprima "Error: [error message]" e retorne
// Se for bem-sucedido, imprima "Account created for [Owner] with balance: $[balance]"
// TODO: Tentar depositar o valor do depósito
// Se houver um erro, imprima "Error: [error message]"
// Se for bem-sucedido, imprima "Deposited successfully. New balance: $[balance]"
// TODO: Tentar sacar o valor do saque
// Se houver um erro, imprima "Error: [error message]"
// Se for bem-sucedido, imprima "Withdrew successfully. New balance: $[balance]"
// Lembre-se de formatar os valores de saldo com duas casas decimais usando fmt.Printf("%.2f", balance)
_ = ownerName
_ = initialDeposit
_ = depositAmount
_ = withdrawAmount
}
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