How to Use a For Loop in Kotlin: Syntax, Ranges, and Compiler Internals
Use for (item in iterable) { ... } to iterate over any object that provides an iterator() operator, including collections, numeric ranges, and arrays.
Kotlin’s for loop is a high-level construct that abstracts iteration behind a unified, readable syntax. According to the JetBrains/kotlin source code, the compiler transforms this syntax into optimized platform-specific code through a multi-stage lowering pipeline involving PSI parsing, FIR-to-IR conversion, and backend-specific code generation.
Basic For Loop Syntax and Iterator Protocol
The for (variable in expression) Pattern
Every Kotlin for loop follows the pattern for (item in collection) { ... }. Unlike C-style for loops, Kotlin’s version iterates over anything that provides an iterator. The compiler requires the expression to have a member or extension function iterator() that returns an object with hasNext() and next() methods.
val fruits = listOf("apple", "banana", "cherry")
for (fruit in fruits) {
println(fruit)
}
Built-in Iterable Types
Kotlin’s standard library provides iterators for common types without requiring explicit imports:
- Collections:
List,Set, andMap(viaentries,keys, orvalues) - Ranges:
IntRange,LongRange, andCharRange - Arrays:
Array<T>,IntArray,CharArray, and primitive variants - Strings: Iterates over
Charsequences
Iterating Over Numeric Ranges and Progressions
Inclusive and Half-Open Ranges
Use the .. operator for inclusive ranges or until for half-open ranges that exclude the end value.
// Inclusive range: prints 1, 2, 3, 4, 5
for (i in 1..5) {
println(i)
}
// Half-open range: prints 0, 1, 2, 3, 4
for (i in 0 until 5) {
println(i)
}
Reverse Iteration and Custom Steps
Use downTo for reverse iteration and step to specify increments other than 1. Under the hood, the compiler generates a progression header via ProgressionLoopHeader.kt and DefaultProgressionHandler.kt to track start, end, step, and direction values.
// Reverse with step: prints 10, 8, 6, 4, 2, 0
for (i in 10 downTo 0 step 2) {
println(i)
}
Advanced Iteration Patterns
Index-Based Iteration with indices
Access array or list indices using the indices property, which returns an IntRange that the compiler optimizes into a simple counter loop.
val values = arrayOf(10, 20, 30)
for (i in values.indices) {
println("Index $i = ${values[i]}")
}
Destructuring in Loop Variables
When iterating over Map entries or Pair collections, use destructuring declarations to unpack values directly in the loop header.
val map = mapOf("a" to 1, "b" to 2)
for ((key, value) in map) {
println("$key -> $value")
}
Custom Iterator Implementation
Any class can be used in a for loop by implementing the iterator() operator function.
class Counter(private val limit: Int) {
operator fun iterator() = object : Iterator<Int> {
var current = 0
override fun hasNext() = current < limit
override fun next() = current++
}
}
for (i in Counter(3)) {
println(i) // Prints: 0, 1, 2
}
Compiler Internals: How For Loops Are Transformed
The Kotlin compiler desugars for loops through several intermediate representations before generating bytecode or machine code. This architecture enables high-level syntax while producing efficient runtime performance.
Parsing and PSI Representation
During the parsing phase, the source syntax for (item in collection) becomes a KtForExpression node in the Program Structure Interface (PSI) tree. This representation is defined in compiler/frontend.common-psi/src/org/jetbrains/kotlin/psi/KtForExpression.kt.
FIR to IR Conversion
In the frontend intermediate representation (FIR), the loop is represented as FirForLoopStatement located at compiler/fir/ir/src/org/jetbrains/kotlin/fir/expressions/FirForLoopStatement.kt. When converting to the backend intermediate representation (IR), this becomes IrForLoop, preserving the high-level loop structure for subsequent optimization passes.
IR Lowering with ForLoopsLowering
The critical transformation occurs in compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/ForLoopsLowering.kt. This pass desugars the high-level for loop into a while-style loop with an explicit iterator or numeric counter. For range-based loops, ProgressionLoopHeader.kt calculates bounds and step values, while DefaultProgressionHandler.kt generates the appropriate iterator logic for numeric progressions.
Platform-Specific Code Generation
After IR lowering, platform-specific backends generate optimized code:
- Kotlin/JVM:
ExpressionCodegen.kt(compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt) emits classicfor-style integer loops (int i = start; i <= end; i += step) for ranges, avoiding iterator allocation overhead. - Kotlin/Native:
KonanBCEForLoopBodyTransformer.kt(kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/KonanBCEForLoopBodyTransformer.kt) applies bounds-check elimination optimizations to array iterations, generating direct pointer arithmetic where safe.
Summary
- Kotlin for loops use the syntax
for (item in iterable)and work with any object providing aniterator()operator, including custom classes. - Numeric ranges support inclusive (
..), half-open (until), reverse (downTo), and stepped (step) iteration patterns. - The compiler optimizes range loops into efficient counter-based loops via
ProgressionLoopHeaderandDefaultProgressionHandler, eliminating iterator object allocation. - Source files including
KtForExpression.kt,FirForLoopStatement.kt, andForLoopsLowering.ktdefine the transformation pipeline from high-level syntax to platform-specific bytecode or machine code.
Frequently Asked Questions
What's the difference between for and forEach in Kotlin?
Both construct loops, but for allows break and continue control flow and may generate tighter bytecode. According to ForLoopsLowering.kt, the compiler applies special optimizations to for loops over ranges and arrays that it cannot apply to forEach lambda invocations, potentially eliminating iterator allocation entirely.
How does Kotlin optimize range-based for loops?
The compiler detects IntRange, LongRange, and CharRange iterations during the lowering phase in ForLoopsLowering.kt. It transforms these into simple while-loops with primitive counters via ProgressionLoopHeader.kt, generating bytecode comparable to Java’s for (int i = 0; i < n; i++) pattern rather than allocating iterator objects.
Can I use a for loop without an iterator?
No. The Kotlin language specification requires the expression in a for loop to provide an iterator() function. To iterate by index without a collection iterator, use withIndex() or indices, both of which return objects that implement the iterator protocol required by the compiler.
How do I iterate backwards with a custom step?
Use the downTo operator combined with step: for (i in 10 downTo 0 step 2). The DefaultProgressionHandler.kt recognizes this pattern and generates a ProgressionLoopHeader with a negative step value, enabling efficient reverse iteration without creating an intermediate reversed collection.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →