Parametr ...T
Umieść ... przed typem ostatniego parametru. Wywołujący mogą wtedy przekazać dowolną liczbę argumentów tego typu, także zero. Wewnątrz funkcji parametr jest slice'em.
To wypisuje 0, 5 i 10. Bez argumentów nums to slice nil o długości 0, więc pętla po prostu się nie wykonuje.
Zwykłe parametry mogą być na początku. Wariadyczny może być tylko ostatni:
func logf(level string, format string, args ...any)
Przekazywanie slice'a przez s...
Jeśli masz już slice, nie możesz przekazać go bezpośrednio: sum(nums) kończy się błędem cannot use nums (variable of type []int) as int value in argument to sum. Rozwiń go przez ... po argumencie:
append też jest wariadyczne, dlatego łączenie dwóch slice'ów to append(a, b...).
Rozwinięcie musi być jedynym elementem na pozycji wariadycznej. sum(1, nums...) kończy się błędem too many arguments in call to sum. Najpierw zbuduj połączony slice przez append.
Slice jest współdzielony, a nie kopiowany
Gdy wywołujesz funkcję z pojedynczymi argumentami, Go tworzy dla nich nowy slice. Gdy rozwijasz istniejący slice przez s..., funkcja dostaje ten sam slice. Zapisy do jego elementów zmieniają dane wywołującego.
Wynik:
[7 8 9]
[0 8 9]
Funkcja wariadyczna, która modyfikuje swój parametr, powinna to udokumentować albo najpierw zrobić kopię przez slices.Clone. Większość funkcji wariadycznych tylko czyta argumenty, więc rzadko sprawia to kłopot, ale to realna różnica względem języków, w których parametry rest to zawsze nowa tablica.
...any i jak działa fmt.Println
fmt.Println jest zadeklarowane jako func Println(a ...any) (n int, err error). any (alias dla interface{} od Go 1.18) przyjmuje każdy typ, więc w jednym wywołaniu możesz przekazać stringi, liczby i struktury.
Ważny szczegół to przekazywanie dalej: logf przekazuje args... do Printf. Bez kropek Printf dostaje jeden argument, []any, a linia z ostrzeżeniem wypisuje [WARN] [3 8] of %!d(MISSING) workers idle. go vet zgłasza missing ... in args forwarded to printf-like function tylko wtedy, gdy parametr formatu jest przekazywany bez zmian, jak w fmt.Printf(format, args). Tutaj format jest budowany przez +, więc vet milczy i o kropki trzeba zadbać samodzielnie.
[]string nie da się rozwinąć do ...any. Typy elementów muszą się dokładnie zgadzać, więc najpierw skonwertuj:
names := []string{"a", "b"}
args := make([]any, len(names))
for i, n := range names {
args[i] = n
}
fmt.Println(args...)
Wymaganie co najmniej jednego argumentu
Parametr wariadyczny przyjmuje zero argumentów. Gdy funkcja potrzebuje co najmniej jednego, zrób z pierwszego zwykły parametr:
Teraz maxOf() to błąd kompilacji, a nie sprawdzenie w czasie działania. Wbudowana funkcja max działa tak samo: wymaga co najmniej jednego argumentu.
Parametry wariadyczne jako argumenty opcjonalne
Go nie ma domyślnych wartości parametrów. Parametr wariadyczny bywa używany do udawania jednego opcjonalnego argumentu:
func connect(addr string, timeout ...time.Duration)
To działa, ale słabo się czyta, bo wywołujący mogą przekazać trzy timeouty, a sygnatura nie mówi, który wygrywa. Przy więcej niż jednej opcji wybierz strukturę konfiguracji albo wzorzec functional options (...Option, gdzie każde Option to funkcja ustawiająca pole). Ten wzorzec jest wariadyczny, ale każdy argument ma jasne znaczenie.
Ściągawka z zasad
| Zasada | Przykład |
|---|---|
| Tylko ostatni parametr może być wariadyczny | func f(a string, b ...int) |
| Wewnątrz parametr jest slice'em | b ma typ []int |
| Zero argumentów daje slice nil | f("x") sprawia, że b == nil |
Rozwiń slice przez ... | f("x", nums...) |
| Rozwinięcie współdzieli tablicę bazową | zapisy do b[i] zmieniają nums |
| Nie można mieszać rozwinięcia z dodatkowymi wartościami | f("x", 1, nums...) się nie kompiluje |
Najczęściej zadawane pytania
Czym jest funkcja wariadyczna w Go?
To funkcja, której ostatni parametr ma postać ...T, na przykład func sum(nums ...int) int. Można ją wywołać z zerem lub większą liczbą argumentów typu T, a wewnątrz funkcji parametr jest slice'em []T.
Jak przekazać slice do funkcji wariadycznej w Go?
Dodaj ... po slice'ie: sum(nums...). Slice jest przekazywany bez zmian i bez kopiowania, więc funkcja widzi tę samą tablicę bazową. Nie można mieszać rozwinięcia z pojedynczymi argumentami: sum(1, nums...) się nie kompiluje.
Czy Go ma operator spread?
Tylko dla ostatniego argumentu wywołania wariadycznego: f(s...). Nie ma ogólnego rozwijania do budowania tablic, struktur ani wywoływania niewariadycznych funkcji ze slice'em. append(a, b...) to ta sama zasada zastosowana do wbudowanego append.
Czy funkcja w Go może mieć dwa parametry wariadyczne?
Nie. Wariadyczny może być tylko ostatni parametr i może być tylko jeden na funkcję. Dodatkowe listy przekazuj jako zwykłe parametry typu slice.