Menu

Funkcje wariadyczne w Golang: parametry ...T i rozwijanie s...

Funkcja wariadyczna w Go przyjmuje dowolną liczbę końcowych argumentów jednego typu. Poznaj składnię ...T, przekazywanie slice'a przez s..., pułapkę ze współdzieleniem i to, jak fmt.Println używa ...any.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

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

ZasadaPrzykład
Tylko ostatni parametr może być wariadycznyfunc f(a string, b ...int)
Wewnątrz parametr jest slice'emb ma typ []int
Zero argumentów daje slice nilf("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ściamif("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.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ