O que é Return em Go?
A instrução return em Go encerra a execução de uma função e opcionalmente retorna um ou mais valores ao chamador. Em Go, o return é particularmente poderoso porque a linguagem suporta múltiplos valores de retorno nativamente — uma característica que fundamenta o idiomático tratamento de erros da linguagem e elimina a necessidade de exceções.
Diferente de linguagens como Java ou Python, onde funções retornam um único valor (ou um objeto wrapper), Go permite que uma função retorne dois, três ou mais valores diretamente. Esse design foi uma decisão deliberada dos criadores da linguagem para tornar o código mais explícito e seguro, especialmente no tratamento de situações de erro.
Para quem está dando os primeiros passos em Go, dominar o return é fundamental. Ele aparece em praticamente toda função que você escreve e interage diretamente com outras construções importantes como defer, goroutines e interfaces.
Retorno Simples
A forma mais básica de return finaliza uma função e retorna um único valor:
func dobro(n int) int {
return n * 2
}
func saudacao(nome string) string {
return fmt.Sprintf("Olá, %s! Bem-vindo ao Go.", nome)
}
func main() {
resultado := dobro(21)
fmt.Println(resultado) // 42
msg := saudacao("Diego")
fmt.Println(msg) // Olá, Diego! Bem-vindo ao Go.
}
Em funções sem valor de retorno (tipo void em outras linguagens), o return sem argumentos pode ser usado para encerrar a execução antecipadamente:
func processar(dados []byte) {
if len(dados) == 0 {
fmt.Println("Sem dados para processar")
return // Encerra a função
}
// Processamento continua...
fmt.Println("Processando", len(dados), "bytes")
}
Múltiplos Valores de Retorno
O suporte a múltiplos valores de retorno é uma das características mais marcantes de Go. Essa funcionalidade permite que funções retornem o resultado junto com informações adicionais, como erros:
func dividir(a, b float64) (float64, error) {
if b == 0 {
return 0, fmt.Errorf("divisão por zero")
}
return a / b, nil
}
func main() {
resultado, err := dividir(10, 3)
if err != nil {
fmt.Println("Erro:", err)
return
}
fmt.Printf("Resultado: %.2f\n", resultado) // Resultado: 3.33
}
Esse padrão (resultado, error) é o idioma mais importante de Go. Você encontra ele em toda a biblioteca padrão e em praticamente todo código Go de produção. Ao contrário de exceções que podem ser ignoradas silenciosamente, retornar erros explicitamente força o desenvolvedor a lidar com cada situação de falha.
Retornando Mais de Dois Valores
Embora o padrão (valor, error) seja o mais comum, Go permite retornar quantos valores forem necessários:
func analisarTexto(texto string) (int, int, int) {
palavras := len(strings.Fields(texto))
caracteres := len(texto)
linhas := strings.Count(texto, "\n") + 1
return palavras, caracteres, linhas
}
func main() {
palavras, caracteres, linhas := analisarTexto("Olá mundo\nGo é incrível")
fmt.Printf("Palavras: %d, Caracteres: %d, Linhas: %d\n",
palavras, caracteres, linhas)
}
Named Returns (Retornos Nomeados)
Go permite dar nomes aos valores de retorno na assinatura da função. Esses nomes criam variáveis locais inicializadas com o valor zero do respectivo tipo:
func buscarUsuario(id int) (nome string, idade int, err error) {
if id <= 0 {
err = fmt.Errorf("ID inválido: %d", id)
return // naked return - retorna nome="", idade=0, err=<erro>
}
// Simula busca no banco
nome = "Maria Silva"
idade = 28
return // naked return - retorna nome="Maria Silva", idade=28, err=nil
}
Os named returns servem como documentação — quem lê a assinatura da função entende imediatamente o que cada valor retornado representa. Porém, o naked return (return sem argumentos) deve ser usado com cautela, pois pode prejudicar a legibilidade em funções longas.
Quando Usar Named Returns
Named returns são recomendados em situações específicas:
// Bom: clarifica o significado dos retornos
func coordenadas() (latitude, longitude float64) {
return -23.5505, -46.6333 // São Paulo
}
// Bom: simplifica retorno em múltiplos pontos de saída com defer
func lerArquivo(caminho string) (conteudo []byte, err error) {
f, err := os.Open(caminho)
if err != nil {
return
}
defer f.Close()
conteudo, err = io.ReadAll(f)
return
}
Interação entre Defer e Return
A relação entre defer e return é sutil e importante. Funções deferidas executam após o return avaliar seus valores, mas antes da função retornar ao chamador. Com named returns, uma função deferida pode até modificar os valores retornados:
func operacao() (resultado int, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("panic recuperado: %v", r)
resultado = -1
}
}()
// Se ocorrer panic, o defer modifica o retorno
resultado = calculoArriscado()
return
}
Esse padrão é amplamente utilizado para tratamento de panics em goroutines e para garantir limpeza de recursos antes de retornar erros.
Padrão Early Return (Guard Clauses)
O early return é um dos padrões mais idiomáticos de Go. Em vez de aninhar blocos if/else, você trata os casos de erro primeiro e retorna imediatamente:
// Ruim: aninhamento excessivo
func processarPedido(pedido *Pedido) error {
if pedido != nil {
if pedido.Valido() {
if pedido.EmEstoque() {
// lógica principal aqui
return nil
} else {
return fmt.Errorf("produto fora de estoque")
}
} else {
return fmt.Errorf("pedido inválido")
}
} else {
return fmt.Errorf("pedido é nil")
}
}
// Bom: guard clauses com early return
func processarPedido(pedido *Pedido) error {
if pedido == nil {
return fmt.Errorf("pedido é nil")
}
if !pedido.Valido() {
return fmt.Errorf("pedido inválido")
}
if !pedido.EmEstoque() {
return fmt.Errorf("produto fora de estoque")
}
// Lógica principal sem aninhamento
return nil
}
Esse padrão mantém o “happy path” alinhado à esquerda e facilita a leitura do código. É uma recomendação oficial do Go Code Review Comments.
Return em Goroutines
É importante entender que return dentro de uma goroutine encerra apenas aquela goroutine, não o programa inteiro. Para comunicar resultados de volta, use channels:
func buscarDados(url string, resultados chan<- string) {
resp, err := http.Get(url)
if err != nil {
resultados <- fmt.Sprintf("erro: %v", err)
return // Encerra apenas esta goroutine
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
resultados <- string(body)
}
func main() {
resultados := make(chan string, 2)
go buscarDados("https://api.exemplo.com/dados", resultados)
go buscarDados("https://api.backup.com/dados", resultados)
fmt.Println(<-resultados) // Primeiro resultado
}
Return com Interfaces
Ao trabalhar com interfaces, return frequentemente retorna tipos concretos onde a assinatura especifica uma interface:
type Repositorio interface {
Buscar(id int) (*Usuario, error)
Salvar(u *Usuario) error
}
func NovoRepositorio(dsn string) (Repositorio, error) {
db, err := sql.Open("postgres", dsn)
if err != nil {
return nil, fmt.Errorf("falha ao conectar: %w", err)
}
return &repositorioPostgres{db: db}, nil
}
Retornar nil para uma interface é válido e representa a ausência de valor — um padrão comum no tratamento de erros em Go.
Boas Práticas com Return
- Sempre trate o retorno de error — nunca use
_para ignorar erros em código de produção - Prefira early return — mantenha o happy path alinhado à esquerda
- Use named returns com moderação — apenas quando clarificam a semântica dos retornos
- Evite naked returns em funções longas — prejudicam a legibilidade
- Documente funções com múltiplos retornos — especialmente quando o significado não é óbvio
- Use
%wpara wrap de erros — permite tratamento de erros comerrors.Iseerrors.As
Perguntas Frequentes (FAQ)
Quantos valores uma função Go pode retornar?
Go não impõe limite técnico ao número de valores de retorno, mas na prática o padrão mais comum é retornar dois valores: o resultado e um error. Três valores são usados em casos como (valor, ok, error). Mais de três valores de retorno indicam que você provavelmente deveria usar uma struct para agrupar os dados retornados.
Qual a diferença entre return explícito e naked return?
O return explícito especifica os valores retornados (return valor, nil), enquanto o naked return retorna os valores atuais dos named returns (return sem argumentos). O naked return só funciona quando a função usa named returns. Para funções curtas (até ~10 linhas), naked returns podem ser aceitáveis; em funções longas, sempre prefira return explícito para manter a legibilidade.
Como o defer interage com return?
Funções deferidas executam após o return avaliar seus valores de retorno, mas antes da função retornar ao chamador. Com named returns, uma função deferida pode modificar os valores retornados — técnica usada para tratamento de panics e limpeza de recursos. Essa ordem de execução é determinística e garantida pela especificação da linguagem.
Por que Go usa return com error em vez de exceções?
Go foi projetado para tratamento de erros explícito via múltiplos retornos em vez de exceções implícitas. Essa decisão torna o fluxo de erros visível no código, força o desenvolvedor a lidar com cada erro no ponto onde ele ocorre, e evita problemas comuns com exceções como stack unwinding inesperado. O padrão if err != nil { return ..., err } é proposital e considerado uma boa prática pela comunidade Go.