Introduction to Loops in D
D provides the familiar C-style while, do-while, and for loops, plus a higher-level foreach loop that iterates directly over arrays, associative arrays, ranges, and any type implementing the range interface (empty, front, popFront). foreach is idiomatic D: it eliminates manual index bookkeeping and works uniformly whether you're iterating a static array, a std.container structure, or a lazily generated range from std.range, making loop code both safer and more expressive than manually indexed for loops.
Cricket analogy: A scorer manually tallying every run ball-by-ball with an index counter is like a C-style for loop, whereas a commentator simply narrating 'next batsman, next batsman' through the order without tracking numbers is like D's foreach iterating a range directly.
while and do-while Loops
A while loop checks its condition before each iteration, so the body may execute zero times, while a do-while loop checks the condition after the body, guaranteeing at least one execution. Both use the same boolean-conversion rules as if statements. do-while is especially useful for menu-driven programs or input-validation loops where you need to perform an action first -- like prompting the user -- before deciding whether to repeat it.
Cricket analogy: A bowler checking the over count before running in is like a while loop testing its condition first -- if six balls are already bowled, no run-up happens -- whereas a nervous debutant who bowls one practice ball regardless before anyone checks the over count is like do-while.
import std.stdio;
void main()
{
int attempts = 0;
while (attempts < 3)
{
writeln("Attempt: ", attempts + 1);
attempts++;
}
int input;
do
{
write("Enter a positive number: ");
input = 5; // simulated input for illustration
} while (input <= 0);
writeln("Got: ", input);
}
The for Loop and foreach
D's for loop mirrors C: an initializer, a condition, and a post-iteration expression, all optional. foreach is D's preferred loop for iterating collections: foreach (item; array) gives you each element by value (or by ref if you add the ref storage class to mutate in place), and foreach (i, item; array) gives you both the index and the element. foreach also works over numeric ranges like foreach (i; 0 .. 10), over associative arrays yielding key/value pairs, and over any user-defined range type, making it the single idiomatic tool for both simple counting loops and custom lazy iteration.
Cricket analogy: A scoreboard operator manually incrementing overs one by one with a counter variable mirrors a classic for loop, while a commentator reading straight down the full batting lineup, naming each player as they come in, mirrors D's foreach over an array.
import std.stdio;
import std.range : iota;
void main()
{
for (int i = 0; i < 5; i++)
writeln("for loop i = ", i);
int[] scores = [88, 92, 76, 95];
foreach (i, score; scores)
writefln("Index %d: score %d", i, score);
foreach (ref score; scores)
score += 5; // ref mutates the array in place
writeln("Boosted scores: ", scores);
foreach (i; 0 .. 10) // range 0..9, upper bound exclusive
write(i, " ");
writeln();
}
Loop Control: break, continue, and Labeled Loops
break exits the nearest enclosing loop or switch immediately; continue skips to the next iteration of the nearest enclosing loop. When loops are nested, D lets you label an outer loop (e.g., outer: for (...) { ... }) and target it explicitly with break outer; or continue outer;, which avoids the awkward boolean-flag workarounds needed in languages without labeled loops. This is particularly useful for searching a nested grid or matrix, where you want to abandon the entire search -- not just the innermost loop -- the moment a match is found.
Cricket analogy: A fielding captain calling off a review immediately when replays clearly show the batsman is out is like break, exiting the process at once, while calling for a re-take of a no-ball delivery without stopping the whole over is like continue.
Labeled break and continue in D require the label to be placed directly before the loop keyword with a colon, e.g. outer: for (...) {}, and the label name must be referenced exactly (break outer;). Forgetting the label only breaks the innermost loop, a common source of subtle bugs when searching nested arrays -- always double-check which loop level a bare break or continue actually targets in nested constructs.
foreach_reverse and Iterating Ranges
foreach_reverse iterates an array or range from the last element to the first without manually reversing the collection or computing indices backward, which is both clearer and typically more efficient than a hand-written reverse for loop. Because D's range concept (types implementing empty, front, popFront, and optionally back/popFront for bidirectional ranges) underlies both foreach and foreach_reverse, any type that models a bidirectional range -- including many types in std.range and std.algorithm -- can be walked backward with foreach_reverse just as naturally as forward with foreach.
Cricket analogy: Reviewing a Test match's fall-of-wickets list from the tenth wicket back to the first, to reconstruct the innings backward, is exactly what foreach_reverse does when walking an array from its last element to its first.
Ranges in D (empty, front, popFront) are the foundation of the entire std.range and std.algorithm ecosystem -- algorithms like map, filter, and take all consume and produce ranges lazily, so a foreach loop over the result of arr.filter!(x => x > 0).map!(x => x * 2) never allocates an intermediate array; each element is computed on demand as the loop pulls it.
- while checks its condition before the loop body runs; do-while checks after, guaranteeing at least one execution.
- The classic for loop gives full manual control over initialization, condition, and increment, just like in C.
- foreach is D's idiomatic loop, working over arrays, associative arrays, numeric ranges, and any user-defined range type.
- Add ref in a foreach parameter list to mutate the underlying collection's elements in place.
- break and continue can target an outer loop directly using labels, avoiding manual boolean flags in nested loops.
- foreach_reverse walks a collection from last element to first without manual index arithmetic.
- D's range concept (empty/front/popFront) underlies foreach and enables lazy, composable algorithms from std.algorithm.
Practice what you learned
1. What is the key difference between while and do-while in D?
2. How do you mutate array elements in place using foreach in D?
3. What does break outer; do in a labeled nested loop?
4. Which range interface methods must a type implement to support D's foreach loop?
5. What does foreach (i; 0 .. 10) iterate over?
Was this page helpful?
You May Also Like
Conditionals in D
Learn how D Programming handles branching logic with if-else, switch, the ternary operator, and compile-time static if.
Functions in D
Understand D function declarations, parameter storage classes (in/ref/out/lazy), overloading, and Design by Contract.
Delegates and Closures
Understand D's delegate type, how closures capture context, and how delegates power callbacks and higher-order functions.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics