Question
In Go, range can iterate over slices, arrays, maps, strings, and channels. Is there a built-in way to iterate directly over a numeric range, similar to this?
for i := range [1..10] {
fmt.Println(i)
}
Also, does Go have a built-in representation for integer ranges similar to Ruby's Range class, or is there another idiomatic way to work with a sequence of numbers?
Short Answer
By the end of this page, you will understand how Go handles numeric iteration, why range does not work like Ruby ranges, and the idiomatic way to loop through integers using Go's for statement. You will also learn how to build your own integer range values when needed.
Concept
Go does not have a built-in integer range type like Ruby's Range, and range in Go does not mean “generate numbers from start to end.”
In Go, range is used to iterate over existing collections or stream-like values, such as:
- arrays
- slices
- maps
- strings
- channels
That means range works over data that already exists. It does not create a sequence of integers for you.
If you want to count from 1 to 10 in Go, the idiomatic solution is a regular for loop:
for i := 1; i <= 10; i++ {
fmt.Println(i)
}
This matters because Go is intentionally simple. Instead of adding a special numeric range syntax, the language relies on one flexible looping construct: for.
So the key idea is:
- Use
rangeto iterate over an existing collection. - Use
forwith start, condition, and increment to iterate through numbers.
If you really need a reusable range-like value, you can create a slice of integers yourself or define your own helper function.
Mental Model
Think of Go's range like walking through items that are already placed in a row on a table.
- A slice: a row of values already exists.
- A map: a set of key-value pairs already exists.
- A string: a sequence of characters already exists.
But a numeric interval like 1..10 is not a row of stored values in Go. It is only an idea: “start here and count until there.”
For that, Go expects you to use a for loop, which is like saying:
- Start at
1 - Keep going while
i <= 10 - Add
1each time
So range walks over something that exists, while for can generate a counting process.
Syntax and Examples
Basic numeric loop
The standard way to iterate over integers in Go is:
for i := 1; i <= 10; i++ {
fmt.Println(i)
}
This means:
- initialize
ito1 - continue while
i <= 10 - increment
iafter each iteration
Starting from zero
for i := 0; i < 5; i++ {
fmt.Println(i)
}
Output:
0
1
2
3
4
Using range with a slice instead
If you create a slice first, then you can use range:
numbers := []int{1, 2, 3, 4, 5}
for index, value := range numbers {
fmt.Println(index, value)
}
Here, range works because numbers is an actual slice.
Creating a helper to build a range-like slice
package main
import "fmt"
func makeRange(start, end int) []int {
result := make([]int, 0, end-start+1)
for i := start; i <= end; i++ {
result = append(result, i)
}
return result
}
func main() {
for _, n := range makeRange(1, 5) {
fmt.Println(n)
}
}
This is useful when you specifically want a collection of integers, but for simple counting, a normal for loop is still the better choice.
Step by Step Execution
Consider this code:
for i := 3; i <= 5; i++ {
fmt.Println(i)
}
Step by step:
-
i := 3- Go creates
iand sets it to3.
- Go creates
-
i <= 53 <= 5is true, so the loop body runs.fmt.Println(i)prints3.
-
i++ibecomes4.
-
i <= 54 <= 5is true.- Print
4.
-
i++ibecomes5.
-
i <= 55 <= 5is true.- Print
5.
-
i++ibecomes6.
-
i <= 56 <= 5is false.- The loop stops.
Output:
3
4
5
This shows that a Go numeric loop is explicit: you control the starting value, stopping condition, and increment.
Real World Use Cases
Numeric iteration appears everywhere in Go programs.
Repeating work a fixed number of times
for i := 0; i < 3; i++ {
fmt.Println("Retrying...")
}
Useful for retry attempts, polling, or repeated tasks.
Processing indexes in a slice
items := []string{"a", "b", "c"}
for i := 0; i < len(items); i++ {
fmt.Println(i, items[i])
}
Helpful when you need index-based access.
Generating test data
for i := 1; i <= 100; i++ {
fmt.Println("user", i)
}
Common in scripts, fixtures, and sample data generation.
Pagination and batching
for page := 1; page <= totalPages; page++ {
fmt.Println("Fetching page", page)
}
Useful in APIs and background jobs.
Time-based counters or simulations
for second := 0; second < 60; second++ {
// simulate one second
}
Used in games, simulations, and system logic.
Real Codebase Usage
In real Go codebases, developers usually prefer the simplest tool that matches the job.
Simple counting with for
For numeric loops, a standard for loop is the default choice:
for i := 0; i < maxRetries; i++ {
err := tryOperation()
if err == nil {
break
}
}
Guarding loop boundaries carefully
Developers often use constants or variables to avoid magic numbers:
const maxWorkers = 5
for i := 0; i < maxWorkers; i++ {
startWorker(i)
}
Index loops when values must be updated
scores := []int{10, 20, 30}
for i := 0; i < len(scores); i++ {
scores[i] += 5
}
This is common when you need to modify elements in place.
range when iterating existing data
for _, user := range users {
fmt.Println(user.Name)
}
This is cleaner than an index-based loop when you only need values.
Helper functions for generated sequences
Some projects create helper functions when a range-like slice improves readability:
func intsBetween(start, end int) []int {
nums := make([]int, 0, end-start+1)
for i := start; i <= end; i++ {
nums = append(nums, i)
}
return nums
}
This is more common in tests, utilities, or data generation than in performance-critical paths.
Common Mistakes
Mistake 1: Expecting range to generate numbers
Broken idea:
for i := range [1..10] {
fmt.Println(i)
}
Why it is wrong:
- Go has no
1..10syntax. rangedoes not generate integer sequences.
Use this instead:
for i := 1; i <= 10; i++ {
fmt.Println(i)
}
Mistake 2: Off-by-one errors
Broken code:
for i := 1; i < 10; i++ {
fmt.Println(i)
}
This prints 1 through 9, not 10.
Correct version:
for i := 1; i <= 10; i++ {
fmt.Println(i)
}
Mistake 3: Using range when you only need a count
Less direct approach:
nums := []int{0, 1, 2, 3, 4}
for i := range nums {
fmt.Println(i)
}
This works, but only because a slice already exists. If you just want to count, a normal for loop is clearer.
Mistake 4: Confusing index and value in range
Broken expectation:
nums := []int{10, 20, 30}
for n := range nums {
fmt.Println(n)
}
This prints indexes, not values:
0
1
2
Correct version:
for _, n := range nums {
fmt.Println(n)
}
Mistake 5: Building slices unnecessarily
If all you need is to print numbers from 1 to 1,000, creating a slice first adds extra memory use.
Prefer:
for i := 1; i <= 1000; i++ {
fmt.Println(i)
}
instead of creating a temporary slice unless you truly need the collection.
Comparisons
| Concept | What it does | Best use case |
|---|---|---|
for i := 1; i <= 10; i++ | Counts through integers | Numeric iteration |
for _, v := range slice | Iterates over existing slice values | Processing collections |
for k, v := range map | Iterates over map entries | Reading maps |
for condition {} | Repeats while condition stays true | Unknown number of iterations |
for vs range
| Feature | for | range |
|---|---|---|
| Generates numbers directly | Yes | No |
| Works on slices and maps | Indirectly | Yes |
| Good for custom step sizes | Yes | No |
| Best for counting | Yes | No |
| Best for existing collections | Sometimes | Yes |
Go vs Ruby style ranges
| Language feature | Go | Ruby |
|---|---|---|
| Built-in numeric range syntax | No | Yes |
| Built-in range type | No | Yes |
| Idiomatic counting loop | for | range-based iteration or loop constructs |
Go chooses explicit loops over special range syntax for integers.
Cheat Sheet
Numeric loop
for i := 1; i <= 10; i++ {
fmt.Println(i)
}
Count from zero
for i := 0; i < n; i++ {
// use i
}
Loop backwards
for i := 10; i >= 1; i-- {
fmt.Println(i)
}
Custom step size
for i := 0; i <= 10; i += 2 {
fmt.Println(i)
}
range over slice
nums := []int{10, 20, 30}
for index, value := range nums {
fmt.Println(index, value)
}
Important rules
- Go has no built-in
1..10syntax. - Go has no built-in integer range type.
- Use
forfor numeric counting. - Use
rangefor existing collections. - In
range, the first value is usually the index or key. - Use
_to ignore unwanted values.
Common edge cases
i < 10stops before10i <= 10includes10rangeon a slice gives indexes and values, not generated integers
FAQ
Why doesn't Go support 1..10 syntax?
Go keeps the language small and explicit. Numeric iteration is handled with for instead of special range syntax.
Can I use range to count from 1 to 10 in Go?
Not directly. range iterates over existing values such as slices, maps, strings, and channels.
Does Go have a built-in Range type like Ruby?
No. If you need a reusable range-like structure, you must build it yourself.
What is the idiomatic way to loop over numbers in Go?
Use a standard for loop:
for i := 1; i <= 10; i++ {
fmt.Println(i)
}
When should I use range instead of for?
Use range when you already have a collection, such as a slice or map, and want to iterate through its elements.
How do I include the end number in a Go loop?
Use <= instead of <.
Can I create my own integer range helper in Go?
Yes. You can write a function that returns a slice of integers or a custom iterator pattern.
Mini Project
Description
Build a small Go program that prints a sequence of integers between two values. This demonstrates the idiomatic way to work with numeric ranges in Go without relying on a built-in Range type.
Goal
Create a reusable function that prints or returns integers from a start value to an end value using standard Go loops.
Requirements
- Write a function that accepts
startandendintegers. - Return a slice containing all integers from
starttoend, inclusive. - Use a
forloop to build the result. - In
main, call the function and print each number. - Handle the case where
startis greater thanendby returning an empty slice.
Keep learning
Related questions
Are Go Maps Passed by Value or Reference?
Learn Go map parameter and return semantics: maps are passed by value, while copies share underlying data. Know when map pointers are useful.
Authentication for Go REST APIs: Sessions, Tokens, and Middleware
Learn practical authentication patterns for Go REST APIs, including password hashing, server sessions, bearer tokens, middleware, and security rules.
Automatic Build Versioning in Go: Embed Incrementing Build Numbers
Learn how to add automatic build versioning in Go using linker flags, build metadata, CI counters, and Git-based version values.