Múltiplos Retornos, Retornos Nomeados e Retornos Nus
Funções Go podem retornar múltiplos valores - geralmente um resultado e um error - e opcionalmente nomear esses resultados na assinatura.
Busque em todas as páginas da documentação
Funções Go podem retornar múltiplos valores - geralmente um resultado e um error - e opcionalmente nomear esses resultados na assinatura.
Retornos nomeados habilitam instruções return nuas e integram-se com defer, mas o uso excessivo esconde o fluxo de dados e frustra os leitores.
Múltiplos valores de retorno são o principal mecanismo de Go para sinalizar falha sem exceções.
Chamadores recebem tuplas (T, error) e tratam erros explicitamente em cada etapa.
Parâmetros de resultado nomeados declaram variáveis com escopo para todo o corpo da função, inicializadas com valores zero na entrada.
Um return nu (sem operandos) retorna os valores atuais desses resultados nomeados.
Use retornos nomeados com moderação: eles brilham em funções curtas com defer que ajustam um erro, e prejudicam a legibilidade em funções longas com muitos branches.
Prefira retornos não nomeados quando a lógica for mais fácil de seguir com instruções explícitas de return valor, err.
Cartão de receita de referência rápida - pronto para copiar e colar.
func ReadConfig(path string) ([]byte, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("ler configuração %s: %w", path, err)
}
return data, nil
}
// Retornos nomeados + defer: ajusta o err na saída (padrão idiomático).
func WriteAtomic(path string, data []byte) (err error) {
f, err := os.CreateTemp(filepath.Dir(path), "tmp-*")
if err != nil {
return err
}
defer func() {
closeErr := f.Close()
if err == nil {
err = closeErr
}
}()
if _, err = io.Copy(f, bytes.NewReader(data)); err != nil {
return err
}
return os.Rename(f.Name(), path)
}Quando usar isso:
error como último valor.defer precisam atualizar o erro de saída após a limpeza (defer + err nomeado).return.package config
import (
"encoding/json"
"fmt"
"os"
)
type Settings struct {
Port int `json:"port"`
Host string `json:"host"`
}
func Load(path string) (s Settings, err error) {
raw, err := os.ReadFile(path)
if err != nil {
return s, fmt.Errorf("config: read: %w", err)
}
if err = json.Unmarshal(raw, &s); err != nil {
return s, fmt.Errorf("config: decode: %w", err)
}
if s.Port == 0 {
return s, fmt.Errorf("config: porta obrigatória")
}
return s, nil
}O que isso demonstra:
%w para errors.Is / errors.As.Settings zero junto com um erro - os chamadores devem verificar err antes de usar s.err nomeado poderia ser combinado com defer em carregadores mais complexos; aqui, retornos explícitos permanecem mais claros.0, "", nil) antes da execução da primeira instrução.return sem argumentos não atribui nada novo - ele sai com os valores nomeados atuais.return liste os valores explicitamente; o compilador impõe a aridade.| Estilo | Escolha quando | Evite quando |
|---|---|---|
Não nomeado (T, error) | A maior parte da lógica de negócios, muitos branches | Nunca - este é o padrão |
Nomeado + return vals explícito | Clareza do Godoc para APIs com muitas tuplas | Corpo da função excede ~40 linhas |
Nomeado + return nu | Curto defer/ajuste de erro | Múltiplos escritores para os mesmos nomes de resultado |
Nomeado apenas para err | defer fecha recursos e define err | err ofuscado em blocos internos |
err Nomeadofunc do() (err error) {
defer func() {
if err != nil {
err = fmt.Errorf("do: %w", err)
}
}()
// ...
return nil
}A closure adiada observa a variável de resultado nomeada por referência (endereçável), então atribuições em defer afetam o valor retornado aos chamadores.
Ofuscar err com := em um bloco interno quebra este padrão - use = no resultado nomeado em vez disso.
// Anti-padrão: retorno nu longe da declaração.
func parseAll(input string) (tokens []string, err error) {
for _, part := range strings.Split(input, ",") {
if part == "" {
return // quais valores? o leitor precisa rolar para cima
}
tokens = append(tokens, part)
}
return
}Prefira return tokens, nil na parte inferior e após os erros para facilitar a leitura.
err nomeado com := - foo, err := interno cria um novo err que o defer não atualizará. Correção: use = no resultado nomeado existente ou evite := em err.return x, y explícito, exceto em helpers defer curtos.err e usar s mesmo assim. Correção: documente "indefinido em caso de erro" ou retorne ponteiros (*Settings, error).defer/erro exigir.error personalizado coletando falhas.| Alternativa | Use quando | Não use quando |
|---|---|---|
Retorno de struct único Result | Muitas saídas correlacionadas | Apenas (T, error) necessário - ruído extra de struct |
Parâmetros de saída por ponteiro func(*T) error | Interoperabilidade C ou mutação de estado grande | Bibliotecas Go idiomáticas - prefira retornos |
panic/recover | Bugs de programador verdadeiramente irrecuperáveis | Falhas esperadas (I/O, validação) |
Tupla (T, bool) ok | Consultas de mapa, verificações de presença | Operações que falham com erros ricos |
Erros são valores no fluxo de controle normal.
Chamadores veem caminhos de falha na ordem fonte sem desenrolamento de pilha oculto.
return sem operandos em uma função que declara parâmetros de resultado nomeados.
Ele retorna o que quer que essas variáveis contenham na saída.
Sim - para seus valores zero de tipo na entrada da função, antes que quaisquer instruções sejam executadas.
Quando a limpeza (fechar, reverter) deve ser executada e pode falhar após um erro primário.
O err nomeado permite que o defer aumente ou preserve a primeira falha.
Raramente.
Nomeie err para padrões defer; mantenha outros resultados sem nome, a menos que o godoc realmente se beneficie.
Não - os tipos de resultado são fixos na assinatura.
Use interfaces, structs ou genéricos para resultados variantes.
Além de três valores correlacionados, prefira uma struct de resultado.
A explosão de aridade torna os locais de chamada propensos a erros.
A convenção coloca error por último: (T, error) ou (int, string, error).
Linters e leitores esperam essa posição.
Válido para ponteiros ou interfaces quando "não encontrado" não é um erro.
Documente a semântica - alguns chamadores confundem nil, nil com bugs.
Nenhuma diferença significativa - eles são um mecanismo em tempo de compilação.
Preocupações com legibilidade dominam a decisão.
Eles se comportam como locais comuns para cobertura.
Guias de estilo (incluindo regras de golangci-lint revive) podem sinalizar retornos nus.
Handlers não retornam nada - eles escrevem em http.ResponseWriter.
Extraia a lógica para funções que retornam (T, error) e mapeie erros para códigos de status no handler.
nil, nil através de interfacesVersões de Stack: Esta página foi escrita para Go 1.26.x (GC padrão Green Tea, go fix modernizers - verificar patch na compilação), chi (última - verificar na compilação), gin (última - verificar na compilação), echo (última - verificar na compilação), google.golang.org/grpc (última - verificar na compilação), sigs.k8s.io/controller-runtime (última - verificar na compilação), kubebuilder (última - verificar na compilação), tinygo (última - verificar alvos de placa na compilação), wazero (última - verificar na compilação) e golangci-lint (última - verificar conjunto de linters na compilação).
Revisado por Chris St. John·Última atualização: 19 de jul. de 2026