Menu

Funciones variádicas en Golang: parámetros ...T y expandir con s...

Una función variádica de Go acepta cualquier número de argumentos finales de un mismo tipo. Aprende la sintaxis ...T, cómo pasar un slice con s..., la trampa del slice compartido y cómo usa fmt.Println el ...any.

Esta página incluye editores ejecutables: edita, ejecuta y ve el resultado al instante.

El parámetro ...T

Pon ... delante del tipo del último parámetro. Quien llama puede pasar entonces cualquier número de argumentos de ese tipo, incluido ninguno. Dentro de la función el parámetro es un slice.

Esto imprime 0, 5 y 10. Sin argumentos, nums es un slice nil de longitud 0, así que el bucle simplemente no se ejecuta.

Los parámetros normales pueden ir primero. Solo el último puede ser variádico:

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

Pasar un slice con s...

Si ya tienes un slice, no puedes pasarlo directamente: sum(nums) falla con cannot use nums (variable of type []int) as int value in argument to sum. Expándelo con ... detrás del argumento:

append también es variádica, y por eso unir dos slices es append(a, b...).

La expansión tiene que ser lo único que haya en la posición variádica. sum(1, nums...) falla con too many arguments in call to sum. Construye antes el slice combinado con append.

El slice se comparte, no se copia

Cuando llamas con argumentos sueltos, Go crea un slice nuevo para ellos. Cuando expandes un slice existente con s..., la función recibe ese mismo slice. Las escrituras en sus elementos cambian los datos de quien llama.

Salida:

[7 8 9]
[0 8 9]

Una función variádica que modifica su parámetro debería documentarlo, o copiarlo antes con slices.Clone. La mayoría de funciones variádicas solo leen sus argumentos, así que rara vez muerde, pero es una diferencia real con los lenguajes donde los parámetros rest son siempre un array nuevo.

...any y cómo funciona fmt.Println

fmt.Println está declarada como func Println(a ...any) (n int, err error). any (un alias de interface{} desde Go 1.18) acepta cualquier tipo, así que puedes pasar strings, números y structs en una sola llamada.

Reenviar es el detalle importante: logf pasa args... a Printf. Sin los puntos, Printf recibe un solo argumento, un []any, y la línea de aviso imprime [WARN] [3 8] of %!d(MISSING) workers idle. go vet informa de missing ... in args forwarded to printf-like function solo cuando el parámetro de formato se pasa sin cambios, como en fmt.Printf(format, args). Aquí el formato se construye con +, así que vet no dice nada y los puntos son cosa tuya.

Un []string no se puede expandir en un ...any. Los tipos de los elementos tienen que coincidir exactamente, así que convierte antes:

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

Exigir al menos un argumento

Un parámetro variádico acepta cero argumentos. Cuando la función necesita al menos uno, haz que el primero sea un parámetro normal:

Ahora maxOf() es un error de compilación en lugar de una comprobación en tiempo de ejecución. La propia función integrada max funciona igual: exige al menos un argumento.

Parámetros variádicos como argumentos opcionales

Go no tiene valores por defecto en los parámetros. A veces se usa un parámetro variádico para simular un argumento opcional:

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

Funciona, pero se lee mal, porque quien llama puede pasar tres timeouts y la firma no dice cuál gana. Para más de una opción, prefiere un struct de configuración o el patrón de opciones funcionales (...Option, donde cada Option es una función que asigna un campo). Ese patrón es variádico, pero cada argumento tiene un significado claro.

Referencia de reglas

ReglaEjemplo
Solo el último parámetro puede ser variádicofunc f(a string, b ...int)
Dentro, el parámetro es un sliceb tiene tipo []int
Cero argumentos dan un slice nilf("x") deja b == nil
Expande un slice con ...f("x", nums...)
La expansión comparte el array subyacentelas escrituras en b[i] cambian nums
No se puede mezclar la expansión con valores extraf("x", 1, nums...) no compila

Preguntas frecuentes

¿Qué es una función variádica en Go?

Una función cuyo último parámetro tiene la forma ...T, como func sum(nums ...int) int. Se puede llamar con cero o más argumentos de tipo T, y dentro de la función el parámetro es un slice []T.

¿Cómo paso un slice a una función variádica en Go?

Añade ... detrás del slice: sum(nums...). El slice se pasa tal cual, sin copiarlo, así que la función ve el mismo array subyacente. No puedes mezclar la expansión con argumentos sueltos: sum(1, nums...) no compila.

¿Tiene Go un operador spread?

Solo para el último argumento de una llamada variádica: f(s...). No existe una expansión general para construir arrays o structs, ni para llamar con un slice a funciones que no son variádicas. append(a, b...) es la misma regla aplicada a la función integrada append.

¿Puede una función de Go tener dos parámetros variádicos?

No. Solo el último parámetro puede ser variádico, y solo puede haber uno por función. Pasa las listas adicionales como parámetros slice normales.

Coddy programming languages illustration

Aprende a programar con Coddy

COMENZAR