<h2>Múltiplos Retornos em Go</h2>
<p>Go é uma das poucas linguagens modernas que suporta nativamente múltiplos retornos de funções. Diferente de linguagens como Python que retornam tuplas ou Java que exigem wrapper objects, em Go você simplesmente declara quantos valores deseja retornar e o compilador cuida do resto. Essa é uma característica fundamental que influencia toda a forma como tratamos erros e valores na linguagem.</p>
<p>O padrão mais comum é retornar um valor útil seguido de um erro. Isso elimina a necessidade de exceções e torna o fluxo de erro explícito no código. Quando você chama uma função que retorna múltiplos valores, é obrigado a lidar com todos eles — não é possível ignorar silenciosamente um erro. Veja como funciona na prática:</p>
<pre><code class="language-go">package main
import (
"fmt"
"strconv"
)
func dividir(a, b float64) (float64, error) {
if b == 0 {
return 0, fmt.Errorf("divisão por zero não permitida")
}
return a / b, nil
}
func buscarUsuario(id int) (string, int, error) {
if id <= 0 {
return "", 0, fmt.Errorf("ID inválido")
}
// Simulando busca em banco de dados
usuarios := map[int]string{1: "Alice", 2: "Bob"}
nome, existe := usuarios[id]
if !existe {
return "", 0, fmt.Errorf("usuário não encontrado")
}
return nome, id, nil
}
func main() {
// Capturando múltiplos retornos
resultado, err := dividir(10, 2)
if err != nil {
fmt.Println("Erro:", err)
} else {
fmt.Printf("Resultado: %.2f\n", resultado)
}
// Descartando retornos com blank identifier
nome, _, err := buscarUsuario(1)
if err != nil {
fmt.Println("Erro:", err)
} else {
fmt.Println("Usuário encontrado:", nome)
}
// Capturando todos os retornos
nome, id, err := buscarUsuario(2)
if err != nil {
fmt.Println("Erro:", err)
} else {
fmt.Printf("Nome: %s, ID: %d\n", nome, id)
}
}</code></pre>
<h3>Nomeando Retornos</h3>
<p>Go permite nomear os valores de retorno na assinatura da função. Quando você faz isso, essas variáveis são inicializadas com seus valores zero automaticamente e podem ser retornadas implicitamente com a instrução <code>return</code> vazia. Isso torna o código mais legível, especialmente em funções com muitos retornos, mas use com moderação — retornos vazios podem obscurecer a lógica se abusados.</p>
<pre><code class="language-go">package main
import "fmt"
// Retornos nomeados - bom para documentação
func calcularMedia(notas []float64) (media float64, total float64, err error) {
if len(notas) == 0 {
err = fmt.Errorf("nenhuma nota fornecida")
return
}
for _, nota := range notas {
total += nota
}
media = total / float64(len(notas))
return
}
// Sem retornos nomeados - mais explícito
func calcularMediaExplicito(notas []float64) (float64, float64, error) {
if len(notas) == 0 {
return 0, 0, fmt.Errorf("nenhuma nota fornecida")
}
var media, total float64
for _, nota := range notas {
total += nota
}
media = total / float64(len(notas))
return media, total, nil
}
func main() {
notas := []float64{7.5, 8.0, 9.5}
media, total, err := calcularMedia(notas)
if err != nil {
fmt.Println("Erro:", err)
} else {
fmt.Printf("Média: %.2f, Total: %.2f\n", media, total)
}
}</code></pre>
<h2>Funções Variádicas</h2>
<p>Funções variádicas são aquelas que aceitam um número indefinido de argumentos do mesmo tipo. Você as declara usando reticências (<code>...</code>) antes do tipo do parâmetro. Internamente, Go converte esses argumentos em um slice, então você manipula como tal. Essa abordagem é muito mais elegante do que passar um slice e evita a necessidade de wrapping manual.</p>
<p>A vantagem principal é a liberdade do chamador: pode passar zero argumentos, um ou vários, tudo com a mesma sintaxe intuitiva. Go usa isso extensivamente, como em <code>fmt.Println()</code> que aceita quantos valores você quiser imprimir.</p>
<pre><code class="language-go">package main
import (
"fmt"
"strings"
)
// Função variádica básica
func somar(numeros ...int) int {
total := 0
for _, num := range numeros {
total += num
}
return total
}
// Variádica com múltiplos retornos
func processarStrings(separador string, textos ...string) (resultado string, contagem int) {
resultado = strings.Join(textos, separador)
contagem = len(textos)
return
}
// Variádica com argumentos fixos antes
func criarMensagem(prefixo string, palavras ...string) string {
return prefixo + ": " + strings.Join(palavras, ", ")
}
func main() {
// Chamadas com diferentes quantidades de argumentos
fmt.Println("Soma de 1,2,3:", somar(1, 2, 3))
fmt.Println("Soma de 5,10:", somar(5, 10))
fmt.Println("Soma vazia:", somar())
// Expandindo um slice com ...
numeros := []int{4, 5, 6}
fmt.Println("Soma do slice:", somar(numeros...))
// Variádica com outros parâmetros
resultado, cont := processarStrings(" | ", "Go", "é", "legal")
fmt.Printf("Resultado: %s (contagem: %d)\n", resultado, cont)
msg := criarMensagem("Linguagens", "Go", "Rust", "Python")
fmt.Println(msg)
}</code></pre>
<h3>Cuidados com Variádicas</h3>
<p>Uma armadilha comum é tentar passar um slice diretamente sem usar o operador de expansão (<code>...</code>). Sem ele, você estará passando o próprio slice como um único argumento, não seus elementos. Além disso, a variádica deve ser sempre o último parâmetro da função — você não pode ter parâmetros após ela.</p>
<pre><code class="language-go">package main
import "fmt"
func imprimirNomes(nomes ...string) {
for _, nome := range nomes {
fmt.Println(nome)
}
}
// ERRADO: variádica não é o último parâmetro
// func errado(nomes ...string, idade int) {} // Isso não compila
func main() {
// Correto: usando operador de expansão
lista := []string{"Alice", "Bob", "Charlie"}
imprimirNomes(lista...)
// Direto com literais
imprimirNomes("Diana", "Eve")
// Sem argumentos (válido)
imprimirNomes()
}</code></pre>
<h2>Funções como Valores</h2>
<p>Em Go, funções são cidadãs de primeira classe — você pode atribuir uma função a uma variável, passá-la como argumento, retorná-la de outra função ou armazená-la em uma estrutura. Isso abre possibilidades poderosas para programação funcional e patterns como callbacks, middlewares e estratégias.</p>
<p>O tipo de uma função é definido pela sua assinatura: quantos e quais parâmetros ela aceita, e quantos e quais valores retorna. Duas funções só são do mesmo tipo se tiverem exatamente a mesma assinatura.</p>
<pre><code class="language-go">package main
import (
"fmt"
"sort"
"strings"
)
// Definindo um tipo de função
type Operacao func(int, int) int
// Função que retorna uma função
func criarMultiplicador(fator int) func(int) int {
return func(x int) int {
return x * fator
}
}
// Função que recebe uma função como argumento
func aplicarOperacao(a, b int, op Operacao) int {
return op(a, b)
}
// Função que filtra usando uma função predicado
func filtrar(numeros []int, predicado func(int) bool) []int {
resultado := []int{}
for _, num := range numeros {
if predicado(num) {
resultado = append(resultado, num)
}
}
return resultado
}
func main() {
// Atribuindo funções a variáveis
somar := func(a, b int) int {
return a + b
}
subtrair := func(a, b int) int {
return a - b
}
fmt.Println("Soma:", aplicarOperacao(10, 5, somar))
fmt.Println("Subtração:", aplicarOperacao(10, 5, subtrair))
// Usando tipo de função definido
var op Operacao
op = func(a, b int) int { return a * b }
fmt.Println("Multiplicação:", aplicarOperacao(10, 5, op))
// Retornando uma função
vezes3 := criarMultiplicador(3)
fmt.Println("10 * 3 =", vezes3(10))
vezes5 := criarMultiplicador(5)
fmt.Println("10 * 5 =", vezes5(10))
// Passando função como argumento
numeros := []int{1, 2, 3, 4, 5, 6, 7, 8}
pares := filtrar(numeros, func(n int) bool { return n%2 == 0 })
fmt.Println("Números pares:", pares)
maiores := filtrar(numeros, func(n int) bool { return n > 4 })
fmt.Println("Maiores que 4:", maiores)
}</code></pre>
<h3>Closures e Captura de Variáveis</h3>
<p>Funções anônimas em Go podem capturar variáveis do escopo externo, criando closures. A captura é por referência, não por valor — se a variável externa muda, a função enxerga a mudança. Essa característica é essencial para patterns como callbacks e decoradores.</p>
<pre><code class="language-go">package main
import "fmt"
func criarContador() func() int {
count := 0
return func() int {
count++
return count
}
}
func aplicarFiltros(palavra string, filtros ...func(string) string) string {
resultado := palavra
for _, filtro := range filtros {
resultado = filtro(resultado)
}
return resultado
}
func main() {
// Closure capturando variável
contador := criarContador()
fmt.Println(contador()) // 1
fmt.Println(contador()) // 2
fmt.Println(contador()) // 3
// Múltiplos contadores independentes
contador2 := criarContador()
fmt.Println(contador2()) // 1 (começa do zero novamente)
// Usando closures como filtros
converterMaiuscula := func(s string) string {
return strings.ToUpper(s)
}
adicionarPrefixo := func(s string) string {
return ">>> " + s
}
resultado := aplicarFiltros("hello", converterMaiuscula, adicionarPrefixo)
fmt.Println(resultado) // >>> HELLO
}</code></pre>
<h3>Armazenando Funções em Estruturas</h3>
<p>Um padrão poderoso é armazenar funções dentro de structs. Isso permite criar objetos com comportamento configurável ou implementar padrões de design como Strategy ou Command.</p>
<pre><code class="language-go">package main
import "fmt"
type Logger struct {
logFunc func(string)
}
type Calculadora struct {
operacao func(int, int) int
nome string
}
func main() {
// Configurando logger com diferentes implementações
loggerConsole := Logger{
logFunc: func(msg string) {
fmt.Println("[CONSOLE]", msg)
},
}
loggerArquivo := Logger{
logFunc: func(msg string) {
fmt.Println("[ARQUIVO]", msg)
},
}
loggerConsole.logFunc("Aplicação iniciada")
loggerArquivo.logFunc("Log escrito em arquivo")
// Calculadora com estratégia plugável
calc1 := Calculadora{
operacao: func(a, b int) int { return a + b },
nome: "Adição",
}
calc2 := Calculadora{
operacao: func(a, b int) int { return a * b },
nome: "Multiplicação",
}
fmt.Printf("%s: %d\n", calc1.nome, calc1.operacao(5, 3))
fmt.Printf("%s: %d\n", calc2.nome, calc2.operacao(5, 3))
}</code></pre>
<h2>Combinando os Três Conceitos</h2>
<p>Agora que você compreende cada mecanismo isoladamente, vamos combiná-los em padrões mais realistas que você encontrará em código profissional.</p>
<pre><code class="language-go">package main
import (
"fmt"
"sort"
)
// Tipo que representa uma transformação
type Transformacao func(string) string
// Função que retorna uma transformação e possível erro
func obterTransformacao(tipo string) (Transformacao, error) {
switch tipo {
case "maiuscula":
return func(s string) string {
return strings.ToUpper(s)
}, nil
case "minuscula":
return func(s string) string {
return strings.ToLower(s)
}, nil
default:
return nil, fmt.Errorf("tipo de transformação desconhecido: %s", tipo)
}
}
// Função que aceita múltiplos transformadores e retorna resultado + contagem
func aplicarTransformacoes(texto string, transformadores ...Transformacao) (string, int) {
resultado := texto
for _, transformador := range transformadores {
resultado = transformador(resultado)
}
return resultado, len(transformadores)
}
// Processador que armazena funções variádicas
type Processador struct {
filtros []func(int) bool
}
func (p *Processador) adicionarFiltro(f func(int) bool) {
p.filtros = append(p.filtros, f)
}
func (p *Processador) processar(numeros ...int) []int {
resultado := []int{}
for _, num := range numeros {
passou := true
for _, filtro := range p.filtros {
if !filtro(num) {
passou = false
break
}
}
if passou {
resultado = append(resultado, num)
}
}
return resultado
}
func main() {
// Combinando retorno múltiplo + funções como valor
transform, err := obterTransformacao("maiuscula")
if err != nil {
fmt.Println("Erro:", err)
return
}
// Usando variádica com funções
resultado, quantidade := aplicarTransformacoes(
"hello world",
transform,
func(s string) string { return ">>> " + s },
)
fmt.Printf("Resultado: %s (%d transformações aplicadas)\n", resultado, quantidade)
// Processador com múltiplos filtros
proc := &Processador{}
proc.adicionarFiltro(func(n int) bool { return n > 5 })
proc.adicionarFiltro(func(n int) bool { return n < 20 })
numeros := []int{1, 6, 10, 15, 25, 30}
filtrados := proc.processar(numeros...)
fmt.Println("Números filtrados:", filtrados)
}</code></pre>
<h2>Referências</h2>
<ul>
<li><a href="https://golang.org/doc/effective_go#functions" target="_blank" rel="noopener noreferrer">Documentação Oficial - Functions</a></li>
<li><a href="https://go.dev/blog/error-handling-and-go" target="_blank" rel="noopener noreferrer">The Go Blog - Error Handling and Go</a></li>
<li><a href="https://golang.org/doc/effective_go#multiple-returns" target="_blank" rel="noopener noreferrer">Effective Go - Multiple Return Values</a></li>
<li><a href="https://gobyexample.com/variadic-functions" target="_blank" rel="noopener noreferrer">Go by Example - Variadic Functions</a></li>
<li><a href="https://gobyexample.com/first-class-functions" target="_blank" rel="noopener noreferrer">Go by Example - First Class Functions</a></li>
</ul>
<p><!-- FIM --></p>