Menu

Pętla for w Go (Golang): wszystkie cztery formy z przykładami

Go ma jedno słowo kluczowe pętli, for, i obejmuje ono wszystko: pętlę z licznikiem i trzema klauzulami, pętlę while z samym warunkiem, pętlę nieskończoną i range po slice'ach, mapach, stringach, kanałach, liczbach i funkcjach. Do tego break i continue z etykietą oraz zmiana zmiennych pętli w Go 1.22.

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

Go ma dokładnie jedno słowo kluczowe pętli: for. Ma ono cztery postacie, które razem pokrywają każdą pętlę, jaką w innych językach piszesz przez for, while, do while lub foreach.

Nagłówka pętli nie otacza się nawiasami okrągłymi, a nawiasy klamrowe są zawsze wymagane.

Pętla z trzema klauzulami

for init; condition; post {
	// body
}
  1. init wykonuje się raz przed pętlą. Zwykle jest to krótka deklaracja zmiennej, jak i := 0, a ta zmienna istnieje tylko wewnątrz pętli.
  2. condition jest sprawdzany przed każdą iteracją. Gdy jest fałszywy, pętla się kończy.
  3. Wykonuje się ciało pętli.
  4. post wykonuje się po każdej iteracji, zwykle jest to i++.

i++ jest w Go instrukcją, a nie wyrażeniem, więc post nie może zawierać i++, j--. Zamiast tego użyj przypisania równoległego: i, j = i+1, j-1.

Wszystkie trzy klauzule są opcjonalne. for ; i < 10; {} jest poprawne, a gofmt przepisuje to na for i < 10 {}, czyli następną formę.

Pętla z samym warunkiem (while w Go)

Z samym warunkiem for zachowuje się jak while w innych językach:

Nie ma osobnego do while. Strona o pętli while pokazuje, jak uzyskać zachowanie "wykonaj przynajmniej raz".

Pętle nieskończone

for bez niczego dalej kręci się w nieskończoność. Wychodzisz z niej przez break, return, os.Exit albo panic:

for {
	line, err := reader.ReadString('\n')
	if err != nil {
		break
	}
	process(line)
}

To standardowy kształt dla serwerów, workerów i pętli wczytujących dane. for {} z pustym ciałem zużywa cały rdzeń procesora; w prawdziwym kodzie ciało na czymś blokuje (odczyt, odbiór z kanału, select).

Pętle range

range iteruje po kolekcji i w każdej iteracji zwraca jedną lub dwie wartości. Co to za wartości, zależy od tego, po czym iterujesz:

Range poPierwsza wartośćDruga wartość
slice lub tablicaindekselement (kopia)
stringindeks bajturuna (znak Unicode)
mapakluczwartość
kanałelement(brak)
liczba całkowita n (Go 1.22)od 0 do n minus 1(brak)
funkcja iteratora (Go 1.23)to, co zwraca funkcjato, co zwraca funkcja

Trzy szczegóły widoczne w wyniku:

  • Zmienna wartości to kopia. v *= 2 wewnątrz pętli nie zmieniłoby slice'a; żeby zmieniać elementy, odwołuj się do slice'a przez indeks, nums[i], tak jak w trzeciej pętli.
  • Indeks stringa to przesunięcie w bajtach. é zajmuje dwa bajty, więc indeks skacze z 1 na 3.
  • Kolejność w mapie jest celowo losowa. Go ją losuje, żeby kod nie mógł na niej polegać. slices.Sorted(maps.Keys(m)) (Go 1.23) daje klucze po kolei.

range jest w pełni omówione na stronie o range, łącznie z kanałami, funkcjami iteratorów i zasadami kopiowania.

break i continue

break kończy najbardziej wewnętrzną pętlę. continue przechodzi do następnej iteracji (w pętli z trzema klauzulami najpierw wykonując post):

break i continue z etykietą

Zwykłe break opuszcza tylko najbardziej wewnętrzną pętlę. Żeby z zagnieżdżonej pętli wyjść z pętli zewnętrznej, oznacz ją etykietą:

Etykieta to identyfikator zakończony dwukropkiem, umieszczony bezpośrednio przed for. Go zgłasza nieużywaną etykietę jako błąd kompilacji, tak samo jak nieużywaną zmienną.

break wewnątrz switch i select

Wewnątrz for instrukcja break w gałęzi switch lub select opuszcza switch lub select, a nie pętlę:

for {
	switch cmd := next(); cmd {
	case "quit":
		break // only exits the switch; the loop keeps running
	}
}

Żeby opuścić pętlę z wnętrza switch lub select, użyj break z etykietą albo return.

Zmienne pętli od Go 1.22

Go 1.22 zmieniło zasięg zmiennych pętli: każda iteracja dostaje teraz nową kopię i i v. Wcześniej przez całą pętlę używana była jedna zmienna, co psuło domknięcia i gorutyny, które ją przechwytywały:

W Go 1.22 i nowszych ten kod wypisuje 0 1 2 oraz [aa bb cc]. Przy starej semantyce ten sam program zapisany z for i := 0; i < 3; i++ (range po liczbie całkowitej nie istniał przed 1.22) wypisywał 3 3 3, a gorutyny ścigały się o wspólne i i name. Dawne obejście, i := i na początku ciała pętli, nie jest już potrzebne.

Zachowanie wybiera się dla każdego modułu linią go w go.mod, a nie wersją kompilatora. Moduł, który nadal ma go 1.21, zachowuje starą semantykę nawet po zbudowaniu w Go 1.24.

Typowe błędy

Modyfikowanie kopii. for _, v := range items { v.Count++ } zmienia kopię. Użyj items[i].Count++ albo iteruj po slice'ie wskaźników.

Dopisywanie podczas iteracji range. range oblicza slice raz, na początku. Elementy dopisane w pętli nie są odwiedzane, więc pętla nadal się kończy, ale nie zobaczy nowych elementów.

Usuwanie ze slice'a po indeksie w pętli do przodu. Usunięcie elementu i przesuwa resztę w lewo, więc następny element zostaje pominięty. Zbuduj nowy slice, iteruj od końca albo użyj slices.DeleteFunc.

Pomyłka o jeden przy <=. for i := 0; i <= len(s); i++ w ostatnim przebiegu czyta s[len(s)] i wywołuje panic z index out of range. Użyj <, a jeszcze lepiej range.

Ciężka praca w warunku. Warunek wykonuje się w każdej iteracji. for i := 0; i < expensiveCount(); i++ za każdym razem wywołuje funkcję; oblicz wynik raz przed pętlą.

Float jako licznik. for x := 0.0; x != 1.0; x += 0.1 nigdy się nie kończy, bo 0.1 nie ma dokładnej reprezentacji binarnej. Licz liczbą całkowitą i z niej obliczaj floata.

Najczęściej zadawane pytania

Jak napisać pętlę for w Go?

Klasyczna forma ma trzy części rozdzielone średnikami i żadnych nawiasów okrągłych:

for i := 0; i < 5; i++ {
	fmt.Println(i)
}

Od Go 1.22 możesz też napisać for i := range 5, co liczy od 0 do 4. Żeby przejść po slice'ie, użyj for i, v := range items.

Czy Go ma pętlę while?

Nie ma słowa kluczowego while, ale for z samym warunkiem to pętla while: for n > 0 { n /= 2 }. for { ... } bez warunku to pętla nieskończona, z której wychodzisz przez break lub return.

Jak przejść pętlą po mapie w Go?

for key, value := range m { ... }. Kolejność iteracji nie jest określona, a środowisko uruchomieniowe ją losuje, więc może się różnić między kolejnymi pętlami. Nigdy na niej nie polegaj. Dla stałej kolejności zbierz klucze, posortuj je przez slices.Sort i iteruj po posortowanych kluczach.

Jak wyjść z zagnieżdżonej pętli w Go?

Postaw etykietę przed zewnętrzną pętlą i podaj ją w break: outer: for ... { for ... { if found { break outer } } }. Zwykłe break opuszcza tylko najbardziej wewnętrzne for, switch lub select. continue outer analogicznie przechodzi do następnej iteracji pętli z etykietą.

Co zmieniło się w zmiennych pętli w Go 1.22?

Przed Go 1.22 pętla for miała jedną zmienną wspólną dla wszystkich iteracji, więc domknięcia i gorutyny uruchomione w pętli często widziały tylko końcową wartość. Od Go 1.22 każda iteracja dostaje własną kopię, więc przechwycenie i lub v w domknięciu działa zgodnie z oczekiwaniami. Nowe zachowanie obowiązuje, gdy go.mod modułu zawiera go 1.22 lub nowszą wersję.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ