Menu

Múltiplos retornos em Golang: (valor, error) e resultados nomeados

Funções em Go podem devolver vários valores. Esta página cobre a sintaxe, a convenção (valor, error), o idioma vírgula-ok, resultados nomeados, return sem valores e o identificador em branco.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

Devolvendo mais de um valor

Liste os tipos dos resultados entre parênteses. Devolva os valores separados por vírgula e receba-os do mesmo jeito.

As funções embutidas min e max existem desde o Go 1.21. Quem chama precisa receber todos os resultados: x := minMax(nums) não compila e dá assignment mismatch: 1 variable but minMax returns 2 values.

A convenção (valor, error)

O motivo mais comum para devolver dois valores é reportar uma falha. Por convenção o erro vem por último, e quando ele não é nil os outros resultados devem ser tratados como sem significado.

Saída:

port: 8080
error: port "http": strconv.Atoi: parsing "http": invalid syntax
error: port out of range

Em caso de falha, devolva o valor zero em todos os outros resultados (0, "", nil). O padrão completo, incluindo empacotamento e verificação, está na página de tratamento de erros.

O idioma vírgula-ok

Várias operações embutidas devolvem um segundo valor opcional, um bool, que diz se o primeiro valor é real. Por convenção a variável se chama ok.

Essas três formas (busca em map, type assertion, recebimento de channel) são especiais: elas devolvem um ou dois valores dependendo de quantas variáveis você atribui. Suas próprias funções não podem fazer isso. Uma função declarada com dois resultados sempre devolve dois.

O identificador em branco

_ recebe um valor e o joga fora. Use-o para resultados de que você não precisa:

_, err := fmt.Sscan(input, &n)
key, _ := parseLine(line)

Ignorar um erro com _ compila, então faça isso só quando souber que a chamada não pode falhar ou que a falha não importa. strings.Builder.WriteString sempre devolve um erro nil, por exemplo; fechar um arquivo depois de escrever nele, não.

Resultados nomeados

Parâmetros de resultado podem ter nome. Eles são declarados no início da função, inicializados com seus valores zero, e um return sem valores devolve o que eles guardam.

Os nomes aparecem na saída do go doc, o que ajuda quando dois resultados têm o mesmo tipo: (quotient, remainder int) diz mais que (int, int).

Um return sem valores em uma função longa é difícil de ler, porque o leitor precisa voltar para ver o que os resultados guardam. O conselho de sempre: nomeie os resultados quando os nomes documentam algo, e ainda assim escreva return quotient, remainder explicitamente, a menos que a função tenha poucas linhas.

Resultados nomeados e defer

Uma função adiada executa depois de a instrução return definir os resultados e antes de quem chama recebê-los. Com resultados nomeados, a função adiada pode lê-los e alterá-los. É assim que você acrescenta contexto a qualquer erro que uma função devolve, ou transforma um panic em erro.

Isto imprime load : empty name e depois <nil>. return errors.New(...) primeiro atribui a err, e depois a função adiada o empacota. Mais sobre isso em defer.

Shadowing de um resultado nomeado

Resultados nomeados vivem no escopo mais externo da função. Um := dentro de um bloco aninhado cria uma variável nova com o mesmo nome, e um return sem valores devolve então a de fora.

func find() (n int, err error) {
	if true {
		n, err := compute() // new n and err, shadowing the results
		_ = n
		_ = err
	}
	return // returns 0, nil
}

O compilador pega o pior caso: um return sem valores dentro do bloco em que um resultado está sombreado falha com result parameter err not in scope at return. Ele não pega a versão acima, em que o return fica fora do bloco. Use = em vez de := quando você se refere às variáveis de resultado.

Erros comuns

  • Usar o valor quando err não é nil. Verifique err primeiro. Uma função pode devolver lixo junto com um erro.
  • Devolver um ponteiro nil tipado como erro. var e *MyErr; return e devolve um error que não é nil. Devolva um nil literal em caso de sucesso.
  • Resultados demais. Três ou mais resultados com significados misturados são difíceis de usar. Devolva uma struct.

Perguntas frequentes

Como devolver vários valores de uma função em Go?

Liste os tipos dos resultados entre parênteses e devolva os valores separados por vírgula: func minMax(xs []int) (int, int) { ...; return lo, hi }. Quem chama atribui todos de uma vez: lo, hi := minMax(nums).

Como ignorar um dos valores de retorno em Go?

Atribua-o ao identificador em branco _: _, err := strconv.Atoi(s) ou n, _ := strconv.Atoi(s). Você não pode simplesmente deixar um valor de fora: o número de variáveis à esquerda precisa bater com o número de resultados.

O que são valores de retorno nomeados em Go?

Parâmetros de resultado que têm nome, como em func split(n int) (half, rest int). Eles começam com seus valores zero, podem receber valores no corpo, e um return sem valores devolve o que eles guardam no momento. Também permitem que uma função adiada altere o que quem chama recebe.

Devo usar return sem valores (naked return) em Go?

Só em funções curtas. Um return sem valores em uma função longa esconde o que está sendo devolvido e facilita bugs de shadowing. Os guias de estilo do Go recomendam nomear os resultados para documentação e devolver valores explícitos em qualquer coisa com mais de algumas linhas.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR