Chapter 5 — Control Flow
This is the chapter where programs start making decisions and going round in circles, which is most of what programs do.
if / else
fn classify(n: int) -> int {
if n < 0 {
return -1
} else if n == 0 {
return 0
} else {
return 1
}
}
Braces are always required — there’s no “one-liner without braces” form to trip over. The
condition must be a bool (we covered why in Chapter 3: no truthiness, ever). else if
chains as far as you need.
loop, break, continue
Ingle’s one looping primitive is loop, which loops forever until you break out of it.
continue skips to the next turn.
fn sum_to(n: int) -> int {
var i = 0
var total = 0
loop {
if i >= n { break }
total = total + i
i = i + 1
}
return total
}
break and continue are only legal inside a loop; using them anywhere else is a compile
error. You might be looking at that loop and thinking “where’s while? where’s the C-style
for?” — and the answer is the next chapter, which has a much nicer for. For counting and
walking over data you’ll reach for that; loop is for the genuinely open-ended cases.
Blocks and scope
Every { } is a scope. A let or var declared inside one lives until that block’s closing
brace and no longer — if, else, loop, and even a bare { } you write yourself all
create a fresh scope. And, as we saw with bindings, an inner name may shadow an outer one:
fn main() -> int {
let x = 1
{
let x = 99 // a different x, just for this block
println("{x}") // 99
}
println("{x}") // 1 — the outer x was never touched
return x
}
This is exactly the block scoping you know from C-family languages, with no surprises. The
shadowing is the one thing C# doesn’t let you do (it forbids a local shadowing another local);
Ingle permits it, because it’s genuinely useful for refining a value step by step, and the
strict immutability of let keeps it from being confusing.