0Pricing
Go Academy · Aula

Envolvendo e desembrulhando errors

Use %w, errors.Is e errors.As

Envolvendo e desembrulhando errors é uma aula grátis de Go Academy no CoddyKit. Esta é a aula 3 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Go Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Go Academy inclui 4 aulas no total.

O que é o encapsulamento de erros?

O encapsulamento de erros insere um erro original dentro de um erro novo, preservando o original para inspeção programática e adicionando contexto. Introduzido de forma adequada no Go 1.13:

package main
import ("fmt"; "errors")

var ErrDB = errors.New("database unreachable")

func queryUsers() error {
    return fmt.Errorf("queryUsers: %w", ErrDB)
}

func loadProfile(id int) error {
    return fmt.Errorf("loadProfile(%d): %w", id, queryUsers())
}

func main() {
    err := loadProfile(42)
    fmt.Println(err) // loadProfile(42): queryUsers: database unreachable
}

O verbo %w

Use %w (não %v) em fmt.Errorf para encapsular um erro. O erro encapsulado pode ser acessado por meio de errors.Unwrap:

package main
import ("fmt"; "errors")

func main() {
    inner := errors.New("inner error")
    outer := fmt.Errorf("outer: %w", inner)

    fmt.Println(outer)              // outer: inner error
    fmt.Println(errors.Unwrap(outer)) // inner error
    fmt.Println(errors.Is(outer, inner)) // true
}

errors.Unwrap

errors.Unwrap retorna o próximo erro da cadeia (o erro encapsulado) ou nil se não houver outro:

package main
import ("fmt"; "errors")

type Layer struct{ msg string; err error }
func (l *Layer) Error() string { return l.msg }
func (l *Layer) Unwrap() error { return l.err }

func main() {
    root := errors.New("root")
    mid  := &Layer{"mid", root}
    top  := &Layer{"top", mid}

    fmt.Println(errors.Unwrap(top))  // mid
    fmt.Println(errors.Unwrap(mid))  // root
    fmt.Println(errors.Unwrap(root)) // <nil>
}

errors.Is — percurso da cadeia

errors.Is(err, target) remove recursivamente o encapsulamento da cadeia de erros até encontrar uma correspondência ou chegar a nil:

package main
import ("fmt"; "errors")

var ErrNotFound = errors.New("not found")

func findItem(id int) error {
    return fmt.Errorf("findItem(%d): %w",
        id, fmt.Errorf("repo: %w", ErrNotFound))
}

func main() {
    err := findItem(7)
    fmt.Println(errors.Is(err, ErrNotFound)) // true — deep in chain
    fmt.Println(err)                         // full message chain
}

errors.As — percurso da cadeia para tipos

errors.As(err, &target) percorre a cadeia procurando um erro atribuível ao tipo do alvo:

package main
import ("fmt"; "errors")

type StatusError struct{ Code int }
func (e *StatusError) Error() string { return fmt.Sprintf("status %d", e.Code) }

func doRequest() error {
    return fmt.Errorf("request: %w", &StatusError{503})
}

func main() {
    err := doRequest()
    var se *StatusError
    if errors.As(err, &se) {
        fmt.Println("status code:", se.Code) // 503
    }
}

Unwrap personalizado para cadeias

Implemente Unwrap() error no seu tipo de erro personalizado para oferecer suporte a errors.Is e errors.As:

package main
import ("fmt"; "errors")

type AppError struct {
    Op  string
    Err error
}
func (e *AppError) Error() string  { return fmt.Sprintf("%s: %v", e.Op, e.Err) }
func (e *AppError) Unwrap() error  { return e.Err }

var ErrAuth = errors.New("unauthorized")

func main() {
    err := &AppError{"login", ErrAuth}
    fmt.Println(errors.Is(err, ErrAuth)) // true
}

%v versus %w em fmt.Errorf

A diferença entre %v e %w:

package main
import ("fmt"; "errors")

func main() {
    inner := errors.New("inner")

    withV := fmt.Errorf("outer: %v", inner) // string only, no wrap
    withW := fmt.Errorf("outer: %w", inner) // wraps inner

    fmt.Println(errors.Is(withV, inner)) // false — not wrapped
    fmt.Println(errors.Is(withW, inner)) // true  — wrapped
}

Encapsulamento na prática: manipulador HTTP

O encapsulamento consistente de erros facilita a depuração em sistemas de produção:

package main
import "fmt"

func dbQuery(sql string) error {
    return fmt.Errorf("db.Query(%q): connection refused", sql)
}

func getUserByID(id int) (string, error) {
    if err := dbQuery("SELECT * FROM users WHERE id=?"); err != nil {
        return "", fmt.Errorf("getUserByID(%d): %w", id, err)
    }
    return "Alice", nil
}

func handler() error {
    _, err := getUserByID(1)
    if err != nil {
        return fmt.Errorf("handler: %w", err)
    }
    return nil
}

func main() { fmt.Println(handler()) }

errors.Join — vários encapsulamentos (Go 1.20+)

errors.Join cria um erro que encapsula vários erros. errors.Is verifica todos eles:

package main
import ("fmt"; "errors")

var ErrA = errors.New("err A")
var ErrB = errors.New("err B")

func main() {
    joined := errors.Join(ErrA, ErrB)
    fmt.Println(joined)
    fmt.Println(errors.Is(joined, ErrA)) // true
    fmt.Println(errors.Is(joined, ErrB)) // true
}

Práticas recomendadas para remover encapsulamentos

Orientações para encapsular erros:

  • Encapsule com %w para preservar o erro para inspeção programática
  • Adicione o nome da operação como contexto: fmt.Errorf("funcName: %w", err)
  • Não encapsule erros sentinela se os chamadores não usarem errors.Is
  • Evite encapsular o mesmo contexto duas vezes em cada nível
  • Implemente Unwrap() nos tipos personalizados para que participem das cadeias

Verificação rápida

Qual é a diferença entre usar %v e %w em fmt.Errorf?

Recapitulação: encapsulamento e remoção de encapsulamento de erros

Resumo:

  • fmt.Errorf("ctx: %w", err) encapsula com contexto
  • errors.Unwrap obtém o próximo erro da cadeia
  • errors.Is percorre a cadeia em busca de uma correspondência de identidade
  • errors.As percorre a cadeia em busca de uma correspondência de tipo
  • Os tipos personalizados implementam Unwrap() error
  • errors.Join encapsula vários erros (Go 1.20+)

Perguntas Frequentes

A aula “Envolvendo e desembrulhando errors” é grátis?

Sim — o texto completo de “Envolvendo e desembrulhando errors” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Go Academy, atualize para CoddyKit PRO. O curso de Go Academy inclui 4 aulas no total.

O que vou aprender em “Envolvendo e desembrulhando errors”?

Use %w, errors.Is e errors.As Você pratica Go Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar Go Academy?

Nenhuma experiência prévia é necessária. Go Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 3 de 4.

Quanto tempo leva a aula “Envolvendo e desembrulhando errors”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de Go Academy?

Sim. Cada aula de Go Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. A interface error
  2. Criando errors personalizados
  3. Envolvendo e desembrulhando errors
  4. panic, recover e defer
← Voltar para Go Academy