Go hat genau ein Schleifen-Schlüsselwort: for. Es hat vier Formen, und zusammen decken sie jede Schleife ab, die du in anderen Sprachen mit for, while, do while oder foreach schreiben würdest.
Um den Schleifenkopf stehen keine Klammern, und geschweifte Klammern sind immer Pflicht.
Die Schleife mit drei Klauseln
for init; condition; post {
// body
}
initläuft einmal vor der Schleife. Meist ist das eine kurze Variablendeklaration wiei := 0, und diese Variable existiert nur innerhalb der Schleife.conditionwird vor jedem Durchlauf geprüft. Ist sie falsch, endet die Schleife.- Der Rumpf läuft.
postläuft nach jedem Durchlauf, typischerweisei++.
i++ ist in Go eine Anweisung, kein Ausdruck, also kann post kein i++, j-- enthalten. Nimm stattdessen eine parallele Zuweisung: i, j = i+1, j-1.
Alle drei Klauseln sind optional. for ; i < 10; {} ist erlaubt, und gofmt schreibt es zu for i < 10 {} um, der nächsten Form.
Schleife nur mit Bedingung (das while von Go)
Nur mit einer Bedingung verhält sich for wie while in anderen Sprachen:
Ein separates do while gibt es nicht. Die Seite zur while-Schleife zeigt, wie du das Verhalten „mindestens einmal ausführen“ bekommst.
Endlosschleifen
for ohne etwas dahinter läuft endlos. Du verlässt die Schleife mit break, return, os.Exit oder einer Panic:
for {
line, err := reader.ReadString('\n')
if err != nil {
break
}
process(line)
}
Das ist die Standardform für Server, Worker und Eingabeschleifen. Ein for {} mit leerem Rumpf verbrennt einen CPU-Kern; in echtem Code blockiert der Rumpf auf etwas (ein Lesen, einen Channel-Empfang, ein select).
range-Schleifen
range iteriert über eine Sammlung und liefert pro Durchlauf einen oder zwei Werte. Welche das sind, hängt davon ab, worüber du iterierst:
| range über | Erster Wert | Zweiter Wert |
|---|---|---|
| Slice oder Array | Index | Element (eine Kopie) |
| String | Byte-Index | Rune (Unicode-Zeichen) |
| Map | Schlüssel | Wert |
| Channel | Element | (keiner) |
Ganzzahl n (Go 1.22) | 0 bis n minus 1 | (keiner) |
| Iterator-Funktion (Go 1.23) | was die Funktion liefert | was die Funktion liefert |
Drei Details, die die Ausgabe zeigt:
- Die Wertvariable ist eine Kopie.
v *= 2in der Schleife würde den Slice nicht ändern; um Elemente zu ändern, indizierst du den Slice mitnums[i], wie in der dritten Schleife. - Der Index eines Strings ist ein Byte-Offset.
ébelegt zwei Bytes, also springt der Index von 1 auf 3. - Die Reihenfolge einer Map ist absichtlich zufällig. Go randomisiert sie, damit Code nicht davon abhängen kann.
slices.Sorted(maps.Keys(m))(Go 1.23) liefert die Schlüssel sortiert.
range behandelt die Seite zu range vollständig, einschließlich Channels, Iterator-Funktionen und der Regeln fürs Kopieren.
break und continue
break beendet die innerste Schleife. continue springt zum nächsten Durchlauf (in einer Schleife mit drei Klauseln läuft vorher post):
break und continue mit Label
Ein einfaches break verlässt nur die innerste Schleife. Um aus einer verschachtelten Schleife eine äußere zu verlassen, gibst du der äußeren ein Label:
Ein Label ist ein Bezeichner mit einem Doppelpunkt, direkt vor dem for. Go meldet ein ungenutztes Label als Compilerfehler, genau wie eine ungenutzte Variable.
break innerhalb von switch und select
Innerhalb eines for verlässt ein break in einem Case von switch oder select das switch oder select, nicht die Schleife:
for {
switch cmd := next(); cmd {
case "quit":
break // only exits the switch; the loop keeps running
}
}
Nimm ein break mit Label oder return, um die Schleife aus einem switch oder select heraus zu verlassen.
Schleifenvariablen seit Go 1.22
Go 1.22 hat den Scope der Schleifenvariablen geändert: Jeder Durchlauf bekommt jetzt eine neue Kopie von i und v. Vorher wurde eine Variable für die ganze Schleife wiederverwendet, und das hat Closures und Goroutinen kaputt gemacht, die sie einfingen:
Mit Go 1.22 und neuer gibt das 0 1 2 und [aa bb cc] aus. Unter der alten Semantik gab dasselbe Programm, geschrieben mit for i := 0; i < 3; i++ (range über eine Ganzzahl gab es vor 1.22 nicht), 3 3 3 aus, und die Goroutinen lieferten sich ein Race um das geteilte i und name. Der alte Workaround, i := i am Anfang des Schleifenrumpfs, ist nicht mehr nötig.
Das Verhalten wird pro Modul über die go-Zeile in go.mod gewählt, nicht über die Version des Compilers. Ein Modul, in dem noch go 1.21 steht, behält die alte Semantik, auch wenn es mit Go 1.24 gebaut wird.
Häufige Fehler
Die Kopie ändern. for _, v := range items { v.Count++ } ändert eine Kopie. Nimm items[i].Count++ oder durchlaufe einen Slice von Pointern.
Beim Durchlaufen anhängen. range wertet den Slice einmal am Anfang aus. Elemente, die in der Schleife angehängt werden, werden nicht besucht. Die Schleife endet also trotzdem, sieht die neuen Einträge aber nicht.
Aus einem Slice per Index in einer Vorwärtsschleife löschen. Das Entfernen von Element i verschiebt den Rest nach links, also wird das nächste Element übersprungen. Bau einen neuen Slice, lauf rückwärts oder nimm slices.DeleteFunc.
Off-by-one mit <=. for i := 0; i <= len(s); i++ liest im letzten Durchlauf s[len(s)] und löst eine Panic mit index out of range aus. Nimm < oder besser range.
Teure Arbeit in der Bedingung. Die Bedingung läuft bei jedem Durchlauf. for i := 0; i < expensiveCount(); i++ ruft die Funktion jedes Mal auf; berechne den Wert einmal vor der Schleife.
Einen Float als Zähler verwenden. for x := 0.0; x != 1.0; x += 0.1 endet nie, weil 0.1 keine exakte binäre Darstellung hat. Zähl mit einer Ganzzahl und berechne den Float daraus.
Häufig gestellte Fragen
Wie schreibt man eine for-Schleife in Go?
Die klassische Form hat drei durch Semikolons getrennte Teile und keine Klammern:
for i := 0; i < 5; i++ {
fmt.Println(i)
}
Seit Go 1.22 kannst du auch for i := range 5 schreiben, das von 0 bis 4 zählt. Um einen Slice zu durchlaufen, nimm for i, v := range items.
Hat Go eine while-Schleife?
Kein Schlüsselwort while, aber for mit nur einer Bedingung ist eine while-Schleife: for n > 0 { n /= 2 }. for { ... } ohne Bedingung ist eine Endlosschleife, die du mit break oder return verlässt.
Wie durchlaufe ich in Go eine Map?
for key, value := range m { ... }. Die Iterationsreihenfolge ist nicht festgelegt, und die Runtime randomisiert sie, sodass sie sich von Schleife zu Schleife unterscheiden kann. Verlass dich nie darauf. Für eine stabile Reihenfolge sammelst du die Schlüssel, sortierst sie mit slices.Sort und durchläufst die sortierten Schlüssel.
Wie verlasse ich in Go eine verschachtelte Schleife?
Setz ein Label vor die äußere Schleife und nenne es im break: outer: for ... { for ... { if found { break outer } } }. Ein einfaches break verlässt nur das innerste for, switch oder select. continue outer springt entsprechend zum nächsten Durchlauf der Schleife mit dem Label.
Was hat sich in Go 1.22 an den Schleifenvariablen geändert?
Vor Go 1.22 hatte eine for-Schleife eine einzige Variable, die sich alle Durchläufe teilten, also sahen Closures und Goroutinen, die in der Schleife gestartet wurden, oft alle den Endwert. Seit Go 1.22 bekommt jeder Durchlauf seine eigene Kopie, also funktioniert das Einfangen von i oder v in einer Closure wie erwartet. Das neue Verhalten gilt, wenn die go.mod des Moduls go 1.22 oder höher angibt.