Question
In Kotlin, iteration helpers such as forEach and repeat are convenient, but break and continue do not seem to work with them the same way they do in normal loops.
For example:
repeat(5) {
break
}
(1..5).forEach {
continue@forEach
}
How can you achieve behavior similar to break and continue when using Kotlin's functional iteration style?
I want to mimic ordinary loops as closely as possible while still using functions like forEach and repeat. I believe this may have worked differently in older Kotlin versions, or there may be a special language feature or annotation involved.
For example, I was expecting something like this to be possible:
public inline fun repeat(times: Int, body: (Int) -> Unit) {
for (index in 0 until times) {
body(index)
}
}
Why does this happen, and what is the correct Kotlin approach?
Short Answer
By the end of this page, you will understand why break and continue do not work directly inside Kotlin functions like forEach and repeat, how inline functions and labeled return behave, and when to use a regular for loop instead. You will also learn practical alternatives for skipping items, stopping iteration early, and writing clear loop logic in real Kotlin code.
Concept
In Kotlin, break and continue are loop-control keywords. They only work with actual loop constructs such as:
forwhiledo...while
Functions like forEach and repeat may look like loops when you read them, but they are still function calls. Their bodies are lambdas, not loop bodies in the language grammar.
That difference matters:
breakmeans: stop the nearest real loop.continuemeans: skip to the next iteration of the nearest real loop.- A lambda passed to
forEachis not itself a loop.
So this does not work:
(1..5).forEach {
break
}
because break can only target a real loop.
What can you do instead?
Kotlin gives you a useful alternative: returns from lambdas.
continue-like behavior
Inside forEach, you can use a labeled return to skip the rest of the current lambda execution and move to the next element:
(1..5).forEach {
if (it == 3) return@forEach
println(it)
}
This prints:
1
2
4
5
That behaves like continue.
break-like behavior
There is no direct break inside forEach. If you want to stop iteration early, common options are:
- use a regular
forloop - use functions like
any,find,firstOrNull, ortakeWhile - use a labeled return from an outer inline function in specific cases
For most code, if you truly need break, the clearest choice is a normal for loop.
Why inline does not change break
Even though forEach and repeat are often inline, that does not make their lambdas become true loop bodies for break and continue. Inline affects function-call behavior and allows certain kinds of return, but it does not redefine the meaning of loop-control keywords.
Why this matters in real code
Understanding this helps you choose the right tool:
- Use
forEachwhen you want to perform an action for every item. - Use
return@forEachwhen you wantcontinue-like skipping. - Use a
forloop when you need realbreakorcontinue. - Use collection operations when your real goal is searching, filtering, or transforming data.
This makes Kotlin code easier to read and avoids fighting the language.
Mental Model
Think of a regular loop as a treadmill with built-in control buttons:
continue= skip this step and move to the next onebreak= stop the treadmill completely
Now think of forEach as hiring a worker to process each item one at a time. You can tell the worker, “stop what you're doing for this item” using return@forEach, but that is not the same as pressing the treadmill's break button. The worker is just inside a function call, not controlling the actual loop keyword machinery.
So:
- Real loop = you control the loop directly
forEachlambda = you control only the current callback execution
That is why return@forEach feels like continue, but there is no direct equivalent of break there.
Syntax and Examples
Real loop syntax
for (i in 1..5) {
if (i == 2) continue
if (i == 4) break
println(i)
}
forEach with continue-like behavior
Use a labeled return:
(1..5).forEach {
if (it == 3) return@forEach
println(it)
}
Explanation
- When
it == 3,return@forEachends the current lambda call. forEachthen moves on to the next item.- This is the closest equivalent to
continue.
forEach cannot use break
This is invalid:
(1..5).forEach {
if (it == 3) break
}
Use a regular loop when you need break
for (i in 1..5) {
if (i == 3) break
println(i)
}
Use another function when your goal is to stop early
If you are searching for something, use firstOrNull:
val result = (1..5).firstOrNull { it == 3 }
println(result)
If you want to process items only while a condition is true, use takeWhile:
(1..10)
.takeWhile { it < 5 }
.forEach { println(it) }
repeat behaves the same way
repeat is also a function, so break is not allowed inside its lambda:
repeat(5) {
if (it == 2) return@repeat
println(it)
}
This skips one iteration, similar to continue. But if you need break, use a for loop:
for (i in 0 until 5) {
if (i == 2) break
println(i)
}
Step by Step Execution
Consider this example:
(1..5).forEach {
if (it == 3) return@forEach
println(it)
}
Step-by-step
1..5creates a range:1, 2, 3, 4, 5forEachstarts calling the lambda for each value.- First value:
it = 1it == 3is falseprintln(1)runs
- Second value:
it = 2it == 3is falseprintln(2)runs
- Third value:
it = 3it == 3is truereturn@forEachexits the lambda for this itemprintln(it)is skipped
- Fourth value:
it = 4- condition is false
println(4)runs
- Fifth value:
it = 5- condition is false
println(5)runs
Output
1
2
4
5
Now compare that with a real break:
for (i in 1..5) {
if (i == 3) break
println(i)
}
What happens here?
i = 1→ print1i = 2→ print2i = 3→breakstops the loop completely4and5are never reached
Output
1
2
This shows the key difference:
return@forEachskips the current callbackbreakstops the whole loop
Real World Use Cases
Skipping invalid data during processing
val names = listOf("Ana", "", "Ben")
names.forEach {
if (it.isBlank()) return@forEach
println(it)
}
Useful when processing user input or CSV rows.
Searching until a match is found
Instead of forcing break into forEach, use a search function:
val userId = listOf(10, 20, 30).firstOrNull { it == 20 }
This is common in APIs and database result handling.
Validating a list of items
val allPositive = listOf(1, 2, 3).all { it > 0 }
This often replaces manual loops that would break on failure.
Processing items until a condition changes
val values = listOf(1, 2, 3, 0, 4, 5)
values.takeWhile { it != 0 }.forEach { println(it) }
Useful in stream-like data processing.
Traditional loop logic in business code
If your logic genuinely needs both continue and break, a standard for loop is usually best:
for (file in files) {
if (!file.exists()) continue
if (file.name == "stop.txt") break
println(file.name)
}
Real Codebase Usage
In real Kotlin projects, developers usually choose iteration style based on intent.
Common patterns
1. Use forEach for side effects on every item
users.forEach { println(it.name) }
This is good when you simply want to do something for each element.
2. Use return@forEach as a guard clause
users.forEach {
if (!it.isActive) return@forEach
sendEmail(it)
}
This is a common pattern for skipping invalid or irrelevant items.
3. Use filter before forEach
users
.filter { it.isActive }
.forEach { sendEmail(it) }
This often reads better than putting skip logic inside forEach.
4. Use firstOrNull, find, any, or all instead of manual early exit
val admin = users.find { it.role == "admin" }
val hasErrors = logs.any { it.level == "ERROR" }
This is very common in production Kotlin because the code expresses the goal directly.
5. Use a for loop when loop control matters
for (token in tokens) {
if (token.isBlank()) continue
if (token == "END") break
process(token)
}
This is common in parsers, input processing, and low-level control flow.
Practical guideline
- Need every element processed? Use
forEach - Need to skip some elements? Use
return@forEachorfilter - Need to stop early? Use
find,any,takeWhile, or aforloop - Need classic loop control? Use a real loop
Common Mistakes
Mistake 1: Trying to use break inside forEach
Broken code:
(1..5).forEach {
if (it == 3) break
}
Why it fails:
breakonly works in real loops.forEachis a function call with a lambda.
How to fix it:
- Use a
forloop, or - use a function that matches your intent, such as
findortakeWhile
Mistake 2: Expecting return@forEach to stop everything
Broken expectation:
(1..5).forEach {
if (it == 3) return@forEach
println(it)
}
Some beginners expect the loop to stop at 3, but it does not. It only skips that one element.
Mistake 3: Using forEach when a normal loop is clearer
Sometimes developers try too hard to keep functional style:
items.forEach {
if (!isValid(it)) return@forEach
// many conditions here
}
If the logic becomes control-flow heavy, use:
for (item in items) {
if (!isValid(item)) continue
// clearer for complex logic
}
Mistake 4: Confusing local and non-local return
In inline lambdas, plain return may try to return from the enclosing function, not just the lambda.
fun test() {
listOf(1, 2, 3).forEach {
if (it == 2) return
}
println("This will not run")
}
This returns from test(), not only from forEach.
To return only from the lambda, use a label:
fun test() {
listOf(1, 2, 3).forEach {
if (it == 2) return@forEach
println(it)
}
println("This will run")
}
Mistake 5: Forgetting more expressive standard functions
Instead of writing loop logic manually:
var found: Int? = null
for (n in 1..10) {
if (n > 5) {
found = n
break
}
}
You can often write:
val found = (1..10).firstOrNull { it > 5 }
Comparisons
| Concept | Works with break | Works with continue | Best for |
|---|---|---|---|
for loop | Yes | Yes | Classic loop control |
while loop | Yes | Yes | Condition-based repetition |
forEach | No | No direct keyword, but return@forEach is similar | Side effects for each item |
repeat | No | No direct keyword, but return@repeat is similar | Fixed-count repetition |
find / firstOrNull | Not needed | Not needed | Stopping when a match is found |
any / all | Not needed | Not needed | Boolean checks over collections |
takeWhile | Not needed | Not needed | Processing until a condition becomes false |
for vs forEach
| Feature | for | forEach |
|---|---|---|
| Language loop construct | Yes | No |
Supports break | Yes | No |
Supports continue | Yes | No |
| Can skip current item | Yes, with continue | Yes, with return@forEach |
| Good for complex control flow | Yes | Usually less ideal |
| Good for concise side-effect code | Sometimes | Yes |
Rule of thumb
- If you need loop control keywords, use
for - If you want concise per-item processing, use
forEach - If your goal is search/filter/validation, prefer collection functions over either style
Cheat Sheet
Quick rules
breakworks only in real loops:for,while,do...whilecontinueworks only in real loopsforEachandrepeatare functions, not loop keywordsreturn@forEachskips the current lambda callreturn@repeatskips the current lambda call- Use
forwhen you need truebreakorcontinue
Common patterns
Skip current item in forEach
items.forEach {
if (shouldSkip(it)) return@forEach
process(it)
}
Stop early when searching
val match = items.firstOrNull { condition(it) }
Stop processing while condition is true
items.takeWhile { condition(it) }.forEach { process(it) }
Use a real loop for classic control
for (item in items) {
if (shouldSkip(item)) continue
if (shouldStop(item)) break
process(item)
}
Edge cases
- Plain
returninside an inline lambda may return from the outer function return@forEachreturns only from the lambda- Inline does not make
breakandcontinuevalid inside lambdas
Best practice
Choose the construct that matches the intent:
- transform data →
map - filter data →
filter - find one item →
find/firstOrNull - check conditions →
any/all - side effects on all items →
forEach - manual loop control →
for
FAQ
Can I use break inside forEach in Kotlin?
No. break only works with actual loop statements like for and while, not inside lambdas passed to functions.
How do I simulate continue in Kotlin forEach?
Use a labeled return:
return@forEach
This skips the rest of the current lambda execution.
How do I stop a forEach early in Kotlin?
Usually, you should use a for loop instead. If your goal is searching or checking a condition, use functions like find, firstOrNull, any, or takeWhile.
Does inline allow break and continue inside lambdas?
No. inline allows some return behavior, but it does not make break or continue valid inside function lambdas.
Why does return@forEach not behave like break?
Because it exits only the current lambda call, not the whole iteration structure.
Should I avoid forEach in Kotlin?
No. forEach is useful for simple per-item actions. But if you need classic loop control, a normal for loop is clearer.
What should I use instead of forEach when I want early exit?
Use:
forfor truebreakfindorfirstOrNullfor searchinganyorallfor boolean checkstakeWhilefor bounded processing
Mini Project
Description
Build a small Kotlin program that processes a list of numbers and demonstrates when to use forEach with return@forEach and when to switch to a normal for loop. This project helps you see the practical difference between skipping the current item and stopping iteration entirely.
Goal
Create a program that skips negative numbers, prints valid numbers, and stops completely when a special stop value is found.
Requirements
- Create a list of integers containing positive numbers, negative numbers, and a stop value such as
0. - Use
forEachonce to print only non-negative numbers by skipping negatives. - Use a normal
forloop once to stop processing when the stop value is reached. - Print clear labels so the output shows the difference between the two approaches.
Keep learning
Related questions
Accessing Kotlin Extension Functions from Java
Learn how Kotlin extension functions are compiled and how to call them correctly from Java with clear examples and common pitfalls.
Add a Maven Repository URL in Gradle Kotlin DSL (build.gradle.kts)
Learn how to add Maven Central and a custom Maven repository URL in Gradle Kotlin DSL using build.gradle.kts syntax.
Allow HTTP and HTTPS in Android 9 Pie with Network Security Configuration
Learn how Android 9 Pie handles cleartext HTTP traffic and how to allow HTTP and HTTPS safely using network security config.