Menu

Funzioni variadiche in Golang: parametri ...T ed espansione s...

Una funzione variadica in Go accetta un numero qualsiasi di argomenti finali di un unico tipo. Scopri la sintassi ...T, come passare uno slice con s..., la trappola dello slice condiviso e come fmt.Println usa ...any.

Questa pagina include editor eseguibili: modifica, esegui e vedi subito l'output.

Il parametro ...T

Metti ... prima del tipo dell'ultimo parametro. Chi chiama la funzione può allora passare un numero qualsiasi di argomenti di quel tipo, anche nessuno. Dentro la funzione il parametro è uno slice.

Stampa 0, 5 e 10. Senza argomenti, nums è uno slice nil di lunghezza 0, quindi il ciclo semplicemente non viene eseguito.

I parametri normali possono venire prima. Solo l'ultimo può essere variadico:

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

Passare uno slice con s...

Se hai già uno slice, non puoi passarlo direttamente: sum(nums) fallisce con cannot use nums (variable of type []int) as int value in argument to sum. Espandilo con ... dopo l'argomento:

Anche append è variadica, ed è per questo che unire due slice si scrive append(a, b...).

L'espansione deve essere l'unica cosa nella posizione variadica. sum(1, nums...) fallisce con too many arguments in call to sum. Costruisci prima lo slice combinato con append.

Lo slice è condiviso, non copiato

Quando chiami la funzione con argomenti singoli, Go crea per loro un nuovo slice. Quando espandi uno slice esistente con s..., la funzione riceve proprio quello slice. Le scritture sui suoi elementi modificano i dati del chiamante.

Output:

[7 8 9]
[0 8 9]

Una funzione variadica che modifica il suo parametro dovrebbe documentarlo, oppure copiarlo prima con slices.Clone. La maggior parte delle funzioni variadiche si limita a leggere gli argomenti, quindi raramente è un problema, ma è una differenza reale rispetto ai linguaggi in cui i parametri rest sono sempre un array nuovo.

...any e come funziona fmt.Println

fmt.Println è dichiarata come func Println(a ...any) (n int, err error). any (un alias di interface{} da Go 1.18) accetta qualsiasi tipo, quindi puoi passare stringhe, numeri e struct nella stessa chiamata.

Il dettaglio importante è l'inoltro: logf passa args... a Printf. Senza i puntini, Printf riceve un solo argomento, un []any, e la riga di avviso stampa [WARN] [3 8] of %!d(MISSING) workers idle. go vet segnala missing ... in args forwarded to printf-like function solo quando il parametro di formato viene passato invariato, come in fmt.Printf(format, args). Qui il formato è costruito con +, quindi vet non dice nulla e i puntini sono una tua responsabilità.

Un []string non si può espandere in ...any. I tipi degli elementi devono coincidere esattamente, quindi converti prima:

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

Richiedere almeno un argomento

Un parametro variadico accetta zero argomenti. Quando la funzione ne richiede almeno uno, rendi il primo un parametro normale:

Ora maxOf() è un errore di compilazione invece di un controllo durante l'esecuzione. La funzione integrata max funziona allo stesso modo: richiede almeno un argomento.

Parametri variadici come argomenti opzionali

Go non ha valori di default per i parametri. A volte si usa un parametro variadico per simulare un argomento opzionale:

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

Funziona ma si legge male, perché chi chiama può passare tre timeout e la firma non dice quale vince. Con più di un'opzione, preferisci una struct di configurazione oppure il pattern functional options (...Option, dove ogni Option è una funzione che imposta un campo). Anche quel pattern è variadico, ma ogni argomento ha un significato chiaro.

Riepilogo delle regole

RegolaEsempio
Solo l'ultimo parametro può essere variadicofunc f(a string, b ...int)
Dentro la funzione il parametro è uno sliceb ha tipo []int
Zero argomenti danno uno slice nilf("x") rende b == nil
Si espande uno slice con ...f("x", nums...)
L'espansione condivide l'array sottostantele scritture su b[i] modificano nums
Non si mescola l'espansione con altri valorif("x", 1, nums...) non compila

Domande frequenti

Cos'è una funzione variadica in Go?

È una funzione il cui ultimo parametro ha la forma ...T, come func sum(nums ...int) int. Si può chiamare con zero o più argomenti di tipo T, e dentro la funzione il parametro è uno slice []T.

Come passo uno slice a una funzione variadica in Go?

Aggiungi ... dopo lo slice: sum(nums...). Lo slice viene passato così com'è, senza copia, quindi la funzione vede lo stesso array sottostante. Non puoi mescolare l'espansione con argomenti singoli: sum(1, nums...) non compila.

Go ha un operatore spread?

Solo per l'ultimo argomento di una chiamata variadica: f(s...). Non esiste uno spread generale per costruire array o struct, o per chiamare funzioni non variadiche con uno slice. append(a, b...) è la stessa regola applicata alla funzione integrata append.

Una funzione Go può avere due parametri variadici?

No. Solo l'ultimo parametro può essere variadico, e ce ne può essere uno per funzione. Passa gli altri elenchi come normali parametri slice.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA