Menu

Funções variádicas em Golang: parâmetros ...T e o espalhamento s...

Uma função variádica em Go aceita qualquer quantidade de argumentos finais de um mesmo tipo. Veja a sintaxe ...T, como passar um slice com s..., a armadilha do slice compartilhado e como o fmt.Println usa ...any.

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

O parâmetro ...T

Coloque ... antes do tipo do último parâmetro. Quem chama pode então passar qualquer quantidade de argumentos daquele tipo, inclusive nenhum. Dentro da função, o parâmetro é um slice.

Isto imprime 0, 5 e 10. Sem argumentos, nums é um slice nil de tamanho 0, então o laço simplesmente não executa.

Parâmetros comuns podem vir antes. Só o último pode ser variádico:

func logf(level string, format string, args ...any)

Passando um slice com s...

Se você já tem um slice, não pode passá-lo diretamente: sum(nums) falha com cannot use nums (variable of type []int) as int value in argument to sum. Espalhe-o com ... depois do argumento:

O append também é variádico, e por isso juntar dois slices é append(a, b...).

O espalhamento precisa ser a única coisa na posição variádica. sum(1, nums...) falha com too many arguments in call to sum. Monte antes o slice combinado com append.

O slice é compartilhado, não copiado

Quando você chama com argumentos avulsos, o Go cria um slice novo para eles. Quando você espalha um slice existente com s..., a função recebe aquele mesmo slice. Escritas nos elementos dele alteram os dados de quem chama.

Saída:

[7 8 9]
[0 8 9]

Uma função variádica que modifica o seu parâmetro deveria documentar isso, ou copiar antes com slices.Clone. A maioria das funções variádicas só lê os argumentos, então isso raramente dá problema, mas é uma diferença real em relação a linguagens em que os parâmetros rest são sempre um array novo.

...any e como o fmt.Println funciona

fmt.Println é declarada como func Println(a ...any) (n int, err error). any (um alias de interface{} desde o Go 1.18) aceita qualquer tipo, então você pode passar strings, números e structs na mesma chamada.

O detalhe importante é o repasse: logf passa args... para o Printf. Sem os pontos, o Printf recebe um único argumento, um []any, e a linha de aviso imprime [WARN] [3 8] of %!d(MISSING) workers idle. O go vet só reporta missing ... in args forwarded to printf-like function quando o parâmetro de formato é repassado sem alteração, como em fmt.Printf(format, args). Aqui o formato é montado com +, então o vet fica quieto e os pontos são responsabilidade sua.

Um []string não pode ser espalhado em ...any. Os tipos dos elementos precisam bater exatamente, então converta antes:

names := []string{"a", "b"}
args := make([]any, len(names))
for i, n := range names {
	args[i] = n
}
fmt.Println(args...)

Exigindo pelo menos um argumento

Um parâmetro variádico aceita zero argumentos. Quando a função precisa de pelo menos um, transforme o primeiro em parâmetro comum:

Agora maxOf() é erro de compilação em vez de uma verificação em tempo de execução. A própria função embutida max funciona assim: ela exige pelo menos um argumento.

Parâmetros variádicos como argumentos opcionais

Go não tem valores padrão para parâmetros. Às vezes um parâmetro variádico é usado para simular um argumento opcional:

func connect(addr string, timeout ...time.Duration)

Funciona, mas fica pouco legível, já que quem chama pode passar três timeouts e a assinatura não diz qual deles vale. Para mais de uma opção, prefira uma struct de configuração ou o padrão functional options (...Option, em que cada Option é uma função que define um campo). Esse padrão é variádico, mas cada argumento tem um significado claro.

Referência das regras

RegraExemplo
Só o último parâmetro pode ser variádicofunc f(a string, b ...int)
Dentro da função, o parâmetro é um sliceb tem tipo []int
Zero argumentos geram um slice nilf("x") faz b == nil
Espalhe um slice com ...f("x", nums...)
O espalhamento compartilha o array subjacenteescritas em b[i] mudam nums
Sem misturar espalhamento com valores extrasf("x", 1, nums...) não compila

Perguntas frequentes

O que é uma função variádica em Go?

Uma função cujo último parâmetro tem a forma ...T, como func sum(nums ...int) int. Ela pode ser chamada com zero ou mais argumentos do tipo T, e dentro da função o parâmetro é um slice []T.

Como passar um slice para uma função variádica em Go?

Acrescente ... depois do slice: sum(nums...). O slice é passado como está, sem cópia, então a função enxerga o mesmo array subjacente. Não dá para misturar o espalhamento com argumentos avulsos: sum(1, nums...) não compila.

Go tem operador spread?

Só para o último argumento de uma chamada variádica: f(s...). Não existe um spread geral para montar arrays, structs ou chamar funções não variádicas com um slice. append(a, b...) é a mesma regra aplicada ao append embutido.

Uma função Go pode ter dois parâmetros variádicos?

Não. Só o último parâmetro pode ser variádico, e só pode haver um por função. Passe listas extras como parâmetros comuns do tipo slice.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR