Loops
Summary
A loop repeats a block of code while a condition holds, or a fixed number
of times, or once per element of a collection. for, while, and
foreach are the standard forms. Loops are how "do this for every enemy
in the level," "keep simulating until the physics step settles," and
"run this every frame" are expressed without writing the same code once
per repetition.
Mental Model
Where a conditional (see Conditional Statements) is a single fork checked
once, a loop is a fork checked repeatedly, with the road looping back
to the same sign after each pass through the block. Every iteration
re-evaluates the condition; the loop keeps running as long as the answer
is "true," and control only moves past the loop once it becomes "false"
(or a break exits it directly).
The three loop forms differ in when the sign is checked and what drives it:
whilechecks a general boolean condition before every iteration — the most flexible form, and the one the others can all be rewritten in terms of.forbundles three things a counting loop always needs — an initializer, a condition, and a per-iteration update — into one line, so a bounded, counted repetition reads as a single unit instead of three separate pieces the reader has to mentally reassemble.foreachhides the counting entirely and just says "once per element of this collection," which is both simpler to read and immune to a whole class of off-by-one bugs that hand-written index management invites.
Core Explanation
while (condition) { ... }checksconditionbefore each iteration, including the first. Ifconditionisfalseimmediately, the body never runs at all.do { ... } while (condition);checksconditionafter each iteration, so the body always runs at least once — useful specifically when "run once, then keep going while still needed" is the actual requirement (a menu loop that must show at least once).for (init; condition; update) { ... }runsinitonce, then repeats: checkcondition, run the body, runupdate. The canonical use is a bounded counting loop (for (int i = 0; i < count; i++)), but any of the three clauses can be more general — the classicforloop is reallywhilewith its bookkeeping made explicit and localized.foreach (var item in collection) { ... }iterates once per element of a collection, in the collection's own order, without exposing an index at all. It cannot skip, reorder, or index backward the way an indexedforcan — the trade for that simplicity is less control.breakexits the nearest enclosing loop immediately, regardless of the loop's condition.continueskips the rest of the current iteration's body and jumps straight to the next condition check (or theupdateclause, in aforloop) — the loop keeps going, just the rest of this pass is skipped.- Nested loops (a loop inside a loop) are common for anything
grid-shaped (iterating rows, then columns) —
break/continueinside a nested loop only affect the innermost loop unless the language provides labeled break/continue, so exiting an outer loop from inside a nested one usually needs an explicit flag or early return instead.
Infinite loops happen when the condition never becomes false —
sometimes intentional (a game's main loop, exited via break or a return,
not by the condition naturally failing), and sometimes a bug (forgetting
to update the variable the condition depends on).
Syntax / Examples
C# (Unity)
// for: bounded counting loop
for (int i = 0; i < enemies.Count; i++)
{
enemies[i].TakeDamage(splashDamage);
}
// foreach: no index management at all
foreach (Enemy enemy in enemies)
{
enemy.TakeDamage(splashDamage);
}
// while: repeats until a general condition fails
while (queue.Count > 0 && !cancelled)
{
ProcessNext(queue.Dequeue());
}
// break / continue
foreach (Enemy enemy in enemies)
{
if (enemy.IsDead) continue; // skip dead enemies
if (enemy.IsBoss) break; // stop entirely once a boss is found
enemy.Update();
}
C++ (Unreal)
for (int32 i = 0; i < Enemies.Num(); ++i)
{
Enemies[i]->TakeDamage(SplashDamage);
}
for (AEnemy* Enemy : Enemies) // range-based for — C++'s foreach
{
Enemy->TakeDamage(SplashDamage);
}
while (Queue.Num() > 0 && !bCancelled)
{
ProcessNext(Queue.Pop());
}
Unreal Blueprint
A For Loop node exposes First Index/Last Index pins and a Loop Body
execution pin, mirroring the counting for. A For Each Loop node
iterates an array directly, matching foreach. A While Loop node
re-checks its condition pin every pass. break has no dedicated node in
standard Blueprint loops — exiting early is typically done with a
Branch that skips remaining logic, a documented difference from the
code forms above.
Practical Example
Finding the closest enemy to the player combines a foreach loop with a
running "best so far" variable — a pattern that recurs constantly:
Enemy closest = null;
float closestDistance = float.MaxValue;
foreach (Enemy enemy in enemies)
{
float distance = Vector3.Distance(player.position, enemy.position);
if (distance < closestDistance)
{
closest = enemy;
closestDistance = distance;
}
}
closest and closestDistance are initialized before the loop and
updated inside it — a "running best" is one of the most common shapes a
loop with accumulated state takes, alongside running totals and counts.
Common Mistakes
- Off-by-one errors —
i <= countinstead ofi < countreads one element past the end of a zero-indexed collection.foreachavoids this category of bug entirely by never exposing an index. - Infinite loops from a condition that never changes — a
whilewhose condition depends on a variable the body never updates never terminates. - Modifying a collection while iterating it with
foreach— most collection types throw at runtime if the collection changes shape (adding/removing elements) mid-iteration; removing matched elements needs either an indexed loop iterated backward, or building a separate list of items to remove and removing them after the loop ends. - Recomputing an expensive value every iteration instead of computing it once outside the loop when it doesn't actually change per-iteration.
- Using
break/continueinside a nested loop expecting it to affect the outer loop — it only ever affects the nearest enclosing loop.
Best Practices
- Prefer
foreachwhenever the index itself isn't needed — it removes an entire category of off-by-one and index-management bugs. - Use
forwhen you genuinely need the index (parallel arrays, iterating backward, iterating only every Nth element). - Never mutate a collection you're iterating with
foreach; collect changes and apply them after the loop, or iterate an explicit index backward when removing matched elements. - Hoist any per-iteration-invariant computation out of the loop body.
- Keep loop bodies focused; extract a named function (see Functions) when a loop body grows past a few lines of clearly separable logic.
Exercises
- Rewrite a
forloop that iterates a list by index into an equivalentforeach, and explain what index-related bug class this removes. - Write a loop that removes every "dead" enemy from a list without
modifying the list while a
foreachiterates it. - Given a
while (count > 0) { ... }loop, identify what must happen inside the body for the loop to ever terminate, and write a version that doesn't (as a deliberate bug), then fix it. - Write a nested loop over a 2D grid that uses
continueto skip already-visited cells andbreakto stop the inner loop once a target is found in the current row.
Related Concepts
- Conditional Statements — a loop's condition is the same kind of boolean expression, just re-checked every iteration instead of once.
- Functions — extracting a loop body into a named function once it grows complex.
References
KNOWLEDGE_SCHEMA.md— GDKB's concept maturity ladder and cross-linking rules.- C# Language Reference — Iteration statements: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/statements/iteration-statements
- cppreference — Range-based for loop: https://en.cppreference.com/w/cpp/language/range-for