ZetCode

Go for loop

last modified July 23, 2026

In this article we show how to create loops in Go with the for statement. We cover C-style loops, single-condition loops, range over collections (slices, maps, strings, integers, channels), nested loops, labels, and the break and continue keywords.

Go for statement

The for statement executes a block of code repeatedly. Unlike many languages, Go has only for for all looping — there is no while or do-while keyword. The for statement comes in several forms: the classic three-component loop, the single-condition loop, the range loop, and the infinite loop.

Classic C style for loop

The following example is similar (not entirely equivalent) to the classic C-style for statement.

main.go
package main

import "fmt"

func main() {

    sum := 0

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

The program calculates the sum of values 1..9.

for i := 0; i < 10; i++ {

    sum += i
}

The for statement consists of three parts: the initialization, the condition, and the increment. The initialization part is executed only once. The body of the for statement is executed when the condition is true. If the condition returns false, the for loop is terminated. After the statements in the block are executed, the for loop switches to the third part, where the counter is incremented. The cycle continues until the condition is not true anymore. Note that it is possible to create endless loops.

$ go run main.go 
45

Decrementing C-style for loop

The C-style for loop can also be used to count downwards.

main.go
package main

import "fmt"

func main() {

    for i := 10; i > 0; i-- {

        fmt.Printf("%d ", i)
    }

    fmt.Println()
}

The program prints numbers from 10 down to 1. Notice that we initialize i to 10, test that i is greater than zero, and decrement with i--.

$ go run main.go 
10 9 8 7 6 5 4 3 2 1 

Single condition for

We can define single condition for statements in Go.

main.go
package main

import "fmt"

func main() {

    sum := 0
    i := 9

    for i > 0 {
        
        sum += i
        i--
    }

    fmt.Println(sum)
}

The single condition for statements are functionally equivalent to the C while loop. We sum the values 9..1. In this example we define the i counter variable.

$ go run main.go 
45

Using range clause

The next example uses the range clause with the for statement.

main.go
package main

import "fmt"

func main() {
    
    nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}

    sum := 0
    
    for _, num := range nums {
    
        sum += num
    }

    fmt.Println(sum)
}

We calculate the sum of integer values.

nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}

We define an array of values.

for _, num := range nums {

    sum += num
}

We iterate over the array with the range clause. The range returns the index and the value in each iteration. Since we do not use the index, we specify the discard _ operator. (The Golang documentation calls it the blank identifier.)

In the next example, we use the index value.

main.go
package main 

import "fmt"

func main() {

    words := []string{"sky", "cup", "cloud", "news", "water"}

    for idx, word := range words {

        fmt.Printf("%s has index %d\n", word, idx)
    }
}

We iterate over a slice of words. We print the word and its index.

$ go run main.go 
sky has index 0
cup has index 1
cloud has index 2
news has index 3
water has index 4

Range over structs

The range clause can iterate over a slice of structs.

main.go
package main

import "fmt"

type Product struct {
    name  string
    price float64
}

func main() {

    products := []Product{
        {"book", 12.50},
        {"pen", 1.80},
        {"ruler", 0.95},
    }

    for _, p := range products {
        fmt.Printf("%s: %.2f\n", p.name, p.price)
    }
}

The example defines a Product struct and iterates over a slice of products, printing the name and price of each.

$ go run main.go
book: 12.50
pen: 1.80
ruler: 0.95

Range over strings

When ranging over a string, Go iterates over Unicode code points (runes), not bytes. This is important for handling multi-byte characters correctly.

main.go
package main

import "fmt"

func main() {

    word := "naïve"

    for idx, ch := range word {

        fmt.Printf("index: %d, char: %c\n", idx, ch)
    }
}

The example iterates over a string containing a multi-byte character "ï". Each iteration returns the byte index and the Unicode code point.

$ go run main.go 
index: 0, char: n
index: 1, char: a
index: 2, char: ï
index: 4, char: v
index: 5, char: e

Notice that the index jumps from 2 to 4 — the character "ï" occupies two bytes.

Range over maps

The range clause can iterate over map keys and values. The iteration order over a map is randomized.

main.go
package main

import "fmt"

func main() {

    capitals := map[string]string{
        "Germany":    "Berlin",
        "Slovakia":   "Bratislava",
        "Poland":     "Warsaw",
        "France":     "Paris",
        "Netherlands": "Amsterdam",
    }

    for country, capital := range capitals {

        fmt.Printf("The capital of %s is %s\n", country, capital)
    }
}

We define a map of countries and their capitals. The for range loop iterates over all key-value pairs.

If we need only the keys, we can omit the value variable:

main.go
package main

import "fmt"

func main() {

    grades := map[string]int{
        "Lucia":  45,
        "Peter":  29,
        "Roman":  41,
        "Zuzana": 36,
    }

    for student := range grades {

        fmt.Println(student)
    }
}

The example iterates over the keys of a map of student grades.

Ranging over integers

In Go version 1.22, a new syntax allowing to range over integers was added.

main.go
package main

import "fmt"

func main() {

    for i := range 5 {
        fmt.Println(i)
    }

    for range 6 {
        fmt.Println("falcon")
    }
}

The example prints values 0..5 and prints falcon word six times.

$ go run main.go
0
1
2
3
4
falcon
falcon
falcon
falcon
falcon
falcon

Range over channels

A for range loop over a channel reads values until the channel is closed. This is a common pattern in concurrent Go programs.

main.go
package main

import "fmt"

func main() {

    msgs := make(chan string)

    go func() {
        msgs <- "hello"
        msgs <- "there"
        msgs <- "!"
        close(msgs)
    }()

    for v := range msgs {
        fmt.Println(v)
    }
}

A goroutine sends three messages into a chan string and then closes it. The for range loop in main receives all values until the channel is closed. If the channel were not closed, the loop would block forever waiting for more values.

$ go run main.go
hello
there
!

Infinite loop / do-while style

An infinite loop is created with for {}. It repeats until a break statement is encountered. The same pattern also simulates a do-while loop — since the condition is checked inside the body, the body is guaranteed to execute at least once.

main.go
package main

import (
    "fmt"
    "math/rand"
)

func main() {

    for {

        r := rand.Intn(20) + 1
        fmt.Printf("%d ", r)

        if r == 10 {
            break
        }
    }

    fmt.Println()
}

The example generates random values from 1 to 20 and prints them. The loop terminates when the value 10 is generated; until then it runs indefinitely. Because the condition is checked after the print statement, the loop body executes at least once — a behavior similar to a do-while loop in other languages.

Nested for loops

For loops can be nested. The inner loop completes all its iterations for each iteration of the outer loop.

main.go
package main

import "fmt"

func main() {

    for i := 1; i <= 10; i++ {

        for j := 1; j <= 10; j++ {

            fmt.Printf("%4d", i*j)
        }

        fmt.Println()
    }
}

The example prints a multiplication table (1 to 10). The outer loop iterates over rows and the inner loop iterates over columns. The %4d format specifier ensures columns are aligned.

Continue keyword

The continue keyword skips the rest of the loop body and continues with the next iteration.

main.go
package main

import "fmt"

func main() {

    for val := range 1000 {

        if val%2 == 0 {
            continue
        }

        fmt.Println(val)

    }
}

The example prints odd numbers from 0 to 999.

Multiple loop variables

Go's for statement supports multiple initialization and post statements separated by commas. This is useful when working with two related variables.

main.go
package main

import "fmt"

func main() {

    words := []string{"sky", "cup", "cloud", "news", "water"}

    for i, j := 0, len(words)-1; i < j; i, j = i+1, j-1 {

        words[i], words[j] = words[j], words[i]
    }

    fmt.Println(words)
}

The example reverses a slice of strings in place. We use two variables: i starts at the beginning and j starts at the end. The loop swaps elements and moves the indices toward the middle.

$ go run main.go 
[water news cloud cup sky]

For loop with labels

Labels allow break and continue to target a specific outer loop when working with nested for statements.

main.go
package main

import "fmt"

func main() {

outer:
    for i := 1; i <= 5; i++ {

        for j := 1; j <= 5; j++ {

            fmt.Printf("%d * %d = %d\n", i, j, i*j)

            if i*j >= 12 {
                break outer
            }
        }
    }

    fmt.Println("finished")
}

The example uses a label outer on the outer loop. When the product reaches 12 or more, the break outer statement terminates both the inner and outer loops.

$ go run main.go 
1 * 1 = 1
1 * 2 = 2
1 * 3 = 3
1 * 4 = 4
1 * 5 = 5
2 * 1 = 2
2 * 2 = 4
2 * 3 = 6
2 * 4 = 8
2 * 5 = 10
3 * 1 = 3
3 * 2 = 6
3 * 3 = 9
3 * 4 = 12
finished

Without the label, a plain break would only exit the inner loop.

Labels can also be used with continue to skip to the next iteration of an outer loop.

main.go
package main

import "fmt"

func main() {

outer:
    for i := 1; i <= 5; i++ {

        for j := 1; j <= 5; j++ {

            if i*j%2 == 0 {
                continue outer
            }

            fmt.Printf("%d * %d = %d\n", i, j, i*j)
        }
    }
}

The example prints only the odd products from the multiplication table. When the product is even, continue outer skips the rest of the inner loop and jumps to the next iteration of the outer loop.

$ go run main.go
1 * 1 = 1
1 * 3 = 3
1 * 5 = 5
3 * 1 = 3
3 * 3 = 9
3 * 5 = 15
5 * 1 = 5
5 * 3 = 15
5 * 5 = 25

Source

The Go Programming Language Specification

In this article we have covered for loops in Golang, including C-style loops, single-condition loops, range over various data structures, nested loops, multiple loop variables, and the use of labels with break and continue.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Go tutorials.