# Chapter 8: Control Flow & Loops Flash has a clean set of control structures, matching Go's syntax structure with Zig's powerful optional unwrapping and compile-time unrolling. --- ## 1. Conditionals (`if` / `else`) In Flash, braces `{}` are mandatory, even for single-statement blocks. Parentheses around the condition are optional. ```flash if value > 100 { log("High") } else { log("Low") } ``` ### Optional Unwrapping in `if` If an expression returns an optional type (`?T`), you can unwrap it in an `if` block using the vertical pipe syntax `|value|`. The block only executes if the value is not `null`. ```flash // If lookup returns a user record, bind it to 'user' and print if lookupByUid(uid) |user| { print(user.name) } else { print("User not found") } ``` ### Value-form `if` Expressions `if` can also be used as an expression (resembling a ternary operator in other languages) to return values. In this form, the `else` branch is mandatory. ```flash // Parens around the condition are required here to avoid parser ambiguity max := if (a > b) a else b ``` --- ## 2. While Loops The `while` loop runs as long as a condition evaluates to `true`. ```flash var i usize = 0 while i < 10 { print_num(i) i += 1 } ``` ### While with Capture When the loop condition yields an optional (`?T`), a `|value|` capture binds the payload on each turn. The loop runs until the expression yields `null` — the idiomatic way to drain an iterator: ```flash // Pull items until the iterator is exhausted while it.next() |item| { process(item) } ``` ### While with `orelse break` You can run a loop and break out when a value resolves to `null` using `orelse break`: ```flash var index usize = 0 while index < argc { arg := argv[index] orelse break print(arg) index += 1 } ``` ### Inline While Loops (`inline while`) An `inline while` loop is unrolled at compile time. This is particularly useful in comptime blocks or function generation to loop over fixed-size values. ```flash comptime var i = 0 inline while i < 4 { // This loop is unrolled at compile time emit(i) i += 1 } ``` --- ## 3. For Loops (`for ITEM in ITER`) Flash utilizes a Go-flavored `for ITEM in ITER` layout. Under the hood, this lowers directly to Zig's `for (iter) |item|`. ```flash var msg = "hello" // Loops over each character in the string slice for char in msg { print_char(char) } ``` ### Ranges A numeric range `lo..hi` iterates the half-open interval `[lo, hi)` — the upper bound is excluded: ```flash // Prints 0, 1, 2, … 9 for i in 0..10 { print_num(i) } ``` ### Index Capture A second capture name gives the zero-based iteration index alongside the element: ```flash // item is each element; idx counts 0, 1, 2, … for item, idx in items { print_kv(idx, item) } ``` Discard either capture you do not need with `_` (for example `for _, idx in items` to walk the indices only). ### Loop `else` Arms A `while` or `for` loop may carry an `else` arm, run when the loop ends **without** hitting a `break` — the search-loop idiom without a flag variable: ```flash for x in xs { if x == needle { break } } else { log("needle is not in xs") } ``` ### Inline For Loops (`inline for`) Like `inline while`, an `inline for` loop is unrolled at compile time — the body is stamped out once per element. Every `for` shape rides along: the range iterator, the indexed second capture, the `_` discard, and the loop `else` arm. ```flash inline for w in .{ 8, 16, 32 } { emit(w) } ``` --- ## 4. Switch A `switch` matches a subject value against one or more prongs. The braces and an exhaustive set of prongs are mandatory; a prong body is either a single expression or a block. ```flash switch state { .ok => log("all good"), .retry, .pending => log("waiting"), // a multi-pattern prong else => log("failed"), } ``` ### Inclusive Ranges (`...`) Inside a prong, `lo...hi` matches an **inclusive** range — note the three dots, distinct from the two-dot slice bound. Like `if`, `switch` is also an expression, so every prong can yield a result value: ```flash grade := switch score { 90...100 => 'A', 80...89 => 'B', else => 'F', } ``` ### Payload Capture When the subject is a tagged union, a `|value|` capture after `=>` binds the active variant's payload: ```flash switch msg { .text => |s| print(s), .code => |n| print_num(n), else => {}, } ``` --- ## 5. Labeled Loops A label on a `while` or `for` is a `break` / `continue` target, so an inner loop can leave or restart an **outer** one directly: ```flash outer: for row in grid { for cell in row { if cell == target { break :outer // leaves the labeled loop } if cell == 0 { continue :outer // restarts the outer loop's next turn } } } ``` The same label grammar already names blocks (`blk: { … break :blk v }`); loops and blocks share it. The label precedes `inline` (`outer: inline while …`), and the compiler resolves every labeled `break` / `continue` lexically — an unknown label, a `continue` aimed at a block label, or a label nothing targets each get a targeted diagnostic. A loop's label is not visible from its `else` arm. --- ## 6. Pointer Captures (`|*x|`) By default a capture binds a **copy** of the element or payload. Prefixing the capture with `*` binds a *pointer* instead, so the body can write in place: ```flash var arr [4]u8 = .{1, 2, 3, 4} // Zero the array in place: p is a pointer to each element for *p in &arr { p.* = 0 } ``` The same `*` works on the `if` / `while` payload captures (`if opt |*x|`, `while it.next() |*v|`) and on switch prongs (`.variant => |*pay|`) — everywhere a pointer capture is meaningful. The `*` rides the element capture only: an index capture stays a value, a `*_` discard is rejected (there is no address worth taking), and error captures (`catch |e|`, `else |e|`) bind by value. The pointer itself is still an immutable capture — `p = q` is rejected; `p.* = v` is the point.