for Loops & range
Reviewed & published by Brayan K
Go is refreshingly minimal: there is exactly one loop keyword, for, and it covers every job — counted loops, while-style loops, infinite loops, and (with range) walking over slices, maps and strings. Master these forms and you can iterate over anything.
Part of the free Go course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.
What You'll Learn in This Lesson
- Write the classic init; condition; post for loop
- Use for as a while loop and as an infinite loop
- Control loops with break and continue
- Walk collections with range (index + value)
- Discard the index with the blank identifier _
- Range over slices, maps and strings
1️⃣ One Keyword, Three Shapes
Every for in Go is a variation on the same keyword. The full form has three parts separated by semicolons: an init statement, a condition, and a post statement. Drop the init and post and you have a while loop. Drop everything and you have an infinite loop you exit with break.
package main
import "fmt"
func main() {
// Go has only ONE loop keyword: for. It wears several hats.
// 1) The classic three-part for: init; condition; post.
for i := 0; i < 5; i++ {
fmt.Print(i, " ") // 0 1 2 3 4
}
fmt.Println()
// 2) "while" loop — just the condition, no init/post.
n := 1
for n < 100 {
n = n * 2 // double until we pass 100
}
fmt.Println("first power of 2 over 100:", n) // 128
// 3) Infinite loop with break.
count := 0
for {
count++
if count == 3 {
break // leave the loop entirely
}
}
fmt.Println("stopped at:", count) // 3
}2️⃣ break, continue & Nesting
break leaves the loop immediately; continue skips the rest of the current iteration and jumps to the next. Loops nest freely — an outer loop for rows, an inner loop for columns. (Go also supports labelled breaks to exit an outer loop from inside an inner one, which you'll meet later.)
package main
import "fmt"
func main() {
// continue skips the REST of the current iteration and moves on.
sum := 0
for i := 1; i <= 10; i++ {
if i%2 != 0 {
continue // skip odd numbers
}
sum += i // add only the evens: 2+4+6+8+10
}
fmt.Println("sum of evens 1..10:", sum) // 30
// Nested loops — a small multiplication grid.
for row := 1; row <= 3; row++ {
for col := 1; col <= 3; col++ {
fmt.Printf("%d ", row*col)
}
fmt.Println()
}
}3️⃣ range — Walk Any Collection
range is how you iterate collections idiomatically. Over a slice it yields the index and the value; over a map it yields the key and value (in random order, by design — never rely on map order); over a string it yields the byte index and the rune. When you don't need the index or key, discard it with the blank identifier _.
package main
import "fmt"
func main() {
// range walks over collections, giving you index AND value.
fruits := []string{"apple", "banana", "cherry"}
for i, fruit := range fruits {
fmt.Printf("%d: %s\n", i, fruit)
}
// Don't need the index? Use the blank identifier _.
total := 0
nums := []int{10, 20, 30}
for _, n := range nums {
total += n
}
fmt.Println("total:", total) // total: 60
// range over a map gives key, value (order is RANDOM by design).
ages := map[string]int{"Ann": 30}
for name, age := range ages {
fmt.Printf("%s is %d\n", name, age) // Ann is 30
}
// range over a string yields byte-index, rune.
for i, r := range "Go!" {
fmt.Printf("%d=%c ", i, r) // 0=G 1=o 2=!
}
fmt.Println()
}Your turn — make a countdown with a three-part loop that decrements.
package main
import "fmt"
func main() {
// 🎯 YOUR TURN — replace each ___ then run it.
// Print the countdown 5, 4, 3, 2, 1.
for i := 5; i ___ 1; i___ { // 👉 >= 1 ; i--
fmt.Print(i, " ")
}
fmt.Println()
// ✅ Expected output: 5 4 3 2 1
}Now find the best score with a range loop. You don't need the index here.
package main
import "fmt"
func main() {
// 🎯 YOUR TURN — finish the range loop then run it.
scores := []int{8, 5, 9, 7}
best := 0
for ___, s := range scores { // 👉 _ (we don't need the index)
if s > best {
best = s
}
}
fmt.Println("best score:", best)
// ✅ Expected output: best score: 9
}🧩 Reorder Challenge
These lines sum a slice with range, but they're scrambled. Put them in order so the program prints 6 (assume a func main wraps them).
total += n
nums := []int{1, 2, 3}
fmt.Println(total)
total := 0
for _, n := range nums {
}- nums := []int{1, 2, 3}
- for _, n := range nums {
- fmt.Println(total)
Why: you must create the slice and the accumulator (total := 0) before the loop can use them. The for header opens the block, the body total += n runs once per element, the brace closes the loop, and only then can you print the final total: 1+2+3 = 6.
Pro Tips
- 💡 Prefer range over manual indexing when walking a whole collection — it's clearer and avoids off-by-one bugs.
- 💡 Never depend on map iteration order. Go randomizes it on purpose; sort the keys first if you need a stable order.
- 💡 Range over a string for characters, not s[i] — indexing gives raw bytes, which breaks on non-ASCII text.
- 💡 Use a labelled break (e.g. break outer) to escape nested loops cleanly instead of a flag variable.
Common Errors (and the fix)
- Infinite loop that never ends — you forgot to advance the counter (the i++ post-statement) or your condition can never become false. Double-check the exit condition.
- "i declared and not used" in a range loop — you took the index but never used it. Replace it with _.
- "cannot range over int" — older Go can't range over a plain number. Range over a slice/map/string, or use a three-part for. (Go 1.22+ does allow for i := range 5.)
- Off-by-one — i <= len(s) reads one past the end. Use i < len(s), or better, range.
📋 Quick Reference
| Goal | Syntax |
|---|---|
| Count 0..n-1 | for i := 0; i < n; i++ { } |
| While | for cond { } |
| Infinite | for { ... break } |
| Slice items | for i, v := range s { } |
| Values only | for _, v := range s { } |
| Map entries | for k, v := range m { } |
🧠 Quick Recall
Predict the output before you reveal the answer.
for i := 0; i < 3; i++ { fmt.Print(i) }
012 — i runs 0, 1, 2 and stops when i reaches 3 because the condition i < 3 is then false.
s := []string{"a","b"}; for i, v := range s { fmt.Print(i, v) }
0 a1 b — range yields index 0 with "a", then index 1 with "b". (Println-style spacing: each pair prints with a space between i and v.)
for i := 1; i <= 4; i++ { if i == 2 { continue }; fmt.Print(i) }
134 — when i is 2, continue skips the print and moves to the next iteration, so 2 is omitted.
Frequently Asked Questions
Q: Where's the while keyword?
Go doesn't have one — a for with only a condition is a while loop: for x < 10 { }. One keyword, fewer surprises.
Q: Why does my map print in a different order each run?
Go deliberately randomizes map iteration order so you never accidentally rely on it. If you need a fixed order, collect the keys into a slice and sort them first.
Q: Can I loop a fixed number of times without a counter variable?
In Go 1.22+ yes: for range 5 { } runs five times. On older versions use a three-part loop and ignore the counter.
Mini-Challenge: Count Vowels
Range over the string, inspect each rune, and tally the vowels. Write it yourself and match the example output.
package main
import "fmt"
func main() {
// 🎯 MINI-CHALLENGE: Count vowels
// Given the word below, count how many vowels (a,e,i,o,u) it has.
// Hint: range over the string to get each rune, then check it
// against a switch or a set of comparisons.
//
// ✅ Example output (for "gopher"): 2 vowels
word := "gopher"
// your code here
}🎉 Lesson Complete!
- ✅ for is Go's only loop — counted, while, and infinite forms
- ✅ break exits; continue skips to the next iteration
- ✅ range yields index+value (slices), key+value (maps), index+rune (strings)
- ✅ Discard what you don't need with the blank identifier _
- ✅ Never rely on map iteration order — it's randomized
Practice quiz
How many loop keywords does Go have?
- three: for, while, do
- two: for and while
- one: for
- none, it uses recursion
Answer: one: for. for is Go's only loop keyword; it covers counted, while, and infinite forms.
What does for i := 0; i < 3; i++ { fmt.Print(i) } print?
- 012
- 123
- 0123
- 0 1 2
Answer: 012. i runs 0, 1, 2 and stops when i reaches 3, printing 012 with no spaces.
How do you write a while loop in Go?
- while cond { }
- loop cond { }
- do { } while cond
- for cond { }
Answer: for cond { }. A for with only a condition is Go's while loop.
What does break do inside a loop?
- skips to the next iteration
- leaves the loop entirely
- restarts the loop
- returns from the function
Answer: leaves the loop entirely. break exits the loop immediately; continue skips to the next iteration.
What does continue do?
- skips the rest of this iteration and moves to the next
- exits the loop
- pauses the loop
- breaks all nested loops
Answer: skips the rest of this iteration and moves to the next. continue jumps to the next iteration, skipping the rest of the current body.
Ranging over a slice with for i, v := range s yields...
- value then index
- key and value
- index and value
- only the value
Answer: index and value. Over a slice, range gives the index and the value.
Ranging over a map yields its entries in what order?
- sorted by key
- random order by design
- insertion order
- reverse order
Answer: random order by design. Go deliberately randomizes map iteration order; never rely on it.
Ranging over a string with for i, r := range s yields...
- rune index and byte
- two runes
- index and string
- byte index and rune
Answer: byte index and rune. Over a string, range yields the byte index and the rune at that position.
How do you discard the index in a range loop?
- omit it: for v := range s
- use the blank identifier: for _, v := range s
- use nil
- use skip
Answer: use the blank identifier: for _, v := range s. Replace the index with the blank identifier _ when you don't need it.
What prints: for i := 1; i <= 4; i++ { if i == 2 { continue }; fmt.Print(i) }?
- 1234
- 234
- 134
- 124
Answer: 134. When i is 2, continue skips the print, so 2 is omitted, giving 134.
Continue this course
- Previous: Control Flow (if, switch)
- Next: Functions and Methods — Define functions with multiple return values and attach methods to types