Menu

Golang variadische Funktionen: ...T-Parameter und s...

Eine variadische Go-Funktion nimmt beliebig viele abschließende Argumente eines Typs an. Die ...T-Syntax, wie du mit s... einen Slice übergibst, die Aliasing-Falle und wie fmt.Println ...any nutzt.

Diese Seite enthält ausführbare Editoren - bearbeiten, ausführen und Ausgabe sofort sehen.

Der ...T-Parameter

Setz ... vor den Typ des letzten Parameters. Aufrufer können dann beliebig viele Argumente dieses Typs übergeben, auch keins. Innerhalb der Funktion ist der Parameter ein Slice.

Das gibt 0, 5 und 10 aus. Ohne Argumente ist nums ein nil-Slice mit Länge 0, also läuft die Schleife einfach nicht.

Normale Parameter können davor stehen. Nur der letzte darf variadisch sein:

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

Einen Slice mit s... übergeben

Wenn du schon einen Slice hast, kannst du ihn nicht direkt übergeben: sum(nums) scheitert mit cannot use nums (variable of type []int) as int value in argument to sum. Spreize ihn mit ... hinter dem Argument:

append ist ebenfalls variadisch. Deshalb verbindest du zwei Slices mit append(a, b...).

Der Spread muss allein an der variadischen Position stehen. sum(1, nums...) scheitert mit too many arguments in call to sum. Bau den kombinierten Slice vorher mit append.

Der Slice wird geteilt, nicht kopiert

Rufst du mit einzelnen Argumenten auf, erzeugt Go für sie einen neuen Slice. Spreizt du einen vorhandenen Slice mit s..., bekommt die Funktion genau diesen Slice. Schreibzugriffe auf seine Elemente ändern die Daten des Aufrufers.

Ausgabe:

[7 8 9]
[0 8 9]

Eine variadische Funktion, die ihren Parameter verändert, sollte das dokumentieren oder vorher mit slices.Clone kopieren. Die meisten variadischen Funktionen lesen ihre Argumente nur, daher beißt das selten, aber es ist ein echter Unterschied zu Sprachen, in denen Rest-Parameter immer ein neues Array sind.

...any und wie fmt.Println funktioniert

fmt.Println ist als func Println(a ...any) (n int, err error) deklariert. any (seit Go 1.18 ein Alias für interface{}) akzeptiert jeden Typ, also kannst du Strings, Zahlen und Structs in einem Aufruf übergeben.

Entscheidend ist das Weiterreichen: logf übergibt args... an Printf. Ohne die Punkte bekommt Printf ein einziges Argument, ein []any, und die Warnzeile lautet [WARN] [3 8] of %!d(MISSING) workers idle. go vet meldet missing ... in args forwarded to printf-like function nur, wenn der Format-Parameter unverändert durchgereicht wird, wie in fmt.Printf(format, args). Hier wird das Format mit + gebaut, also schweigt vet, und die Punkte sind deine Verantwortung.

Ein []string lässt sich nicht in ...any spreizen. Die Elementtypen müssen exakt passen, also konvertierst du vorher:

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

Mindestens ein Argument verlangen

Ein variadischer Parameter akzeptiert null Argumente. Braucht die Funktion mindestens eins, mach das erste zu einem normalen Parameter:

Jetzt ist maxOf() ein Compilerfehler statt einer Prüfung zur Laufzeit. Das eingebaute max funktioniert genauso: Es verlangt mindestens ein Argument.

Variadische Parameter als optionale Argumente

Go hat keine Standardwerte für Parameter. Manchmal wird ein variadischer Parameter benutzt, um ein optionales Argument nachzubilden:

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

Das funktioniert, liest sich aber schlecht, weil Aufrufer drei Timeouts übergeben können und die Signatur nicht sagt, welcher gewinnt. Für mehr als eine Option nimm lieber ein Config-Struct oder das Functional-Options-Pattern (...Option, wobei jede Option eine Funktion ist, die ein Feld setzt). Dieses Pattern ist variadisch, aber jedes Argument hat eine klare Bedeutung.

Regeln im Überblick

RegelBeispiel
Nur der letzte Parameter darf variadisch seinfunc f(a string, b ...int)
Innen ist der Parameter ein Sliceb hat den Typ []int
Null Argumente ergeben einen nil-Slicef("x") ergibt b == nil
Einen Slice mit ... spreizenf("x", nums...)
Spread teilt das Backing-ArraySchreiben auf b[i] ändert nums
Kein Mischen von Spread mit weiteren Wertenf("x", 1, nums...) kompiliert nicht

Häufig gestellte Fragen

Was ist eine variadische Funktion in Go?

Eine Funktion, deren letzter Parameter die Form ...T hat, etwa func sum(nums ...int) int. Sie lässt sich mit null oder mehr Argumenten vom Typ T aufrufen, und innerhalb der Funktion ist der Parameter ein []T-Slice.

Wie übergebe ich einen Slice an eine variadische Funktion in Go?

Häng ... an den Slice: sum(nums...). Der Slice wird so übergeben, wie er ist, ohne Kopie, also sieht die Funktion dasselbe Backing-Array. Spread und einzelne Argumente lassen sich nicht mischen: sum(1, nums...) kompiliert nicht.

Hat Go einen Spread-Operator?

Nur für das letzte Argument eines variadischen Aufrufs: f(s...). Einen allgemeinen Spread zum Bauen von Arrays oder Structs oder zum Aufruf nicht variadischer Funktionen mit einem Slice gibt es nicht. append(a, b...) ist dieselbe Regel, angewendet auf das eingebaute append.

Kann eine Go-Funktion zwei variadische Parameter haben?

Nein. Nur der letzte Parameter darf variadisch sein, und davon gibt es einen pro Funktion. Weitere Listen übergibst du als gewöhnliche Slice-Parameter.

Coddy programming languages illustration

Lerne mit Coddy zu programmieren

LOS GEHT'S