Control flow
Branch, loop, and match on values.
On this page
Use if to branch, while and each to loop, and match to pick one of several cases. Conditions must be a bool.
If and else#
fn describe(score: int) String {
if score >= 80 {
return "Excellent"
} else if score >= 50 {
return "Passed"
} else {
return "Try again"
}
}
fn main() {
println(describe(72)) // Passed
let score = 30
if score < 50 : println("Low") // one statement after a colon
println(score > 0 ? "positive" : "zero or less")
}
if x : statement takes a single statement. cond ? a : b picks one of two values.
While#
fn main() {
let remaining = 3
while remaining > 0 {
print(remaining)
remaining--
}
println("") // 321
}
The condition is checked before each round, so the body can run zero times. Use while true { ... } with break for a loop that ends in the middle.
Each#
each visits the items of a collection, with an optional index:
fn main() {
let tasks = Array[String]{ "Build", "Test", "Ship" }
each tasks as task, index {
println("%{index + 1}. %task")
}
let stock = Map[int]{ "pen" => 3 }
each stock as count, item : println("%item: %count") // pen: 3
each tasks skip 1 as task : print(task + " ") // Test Ship
println("")
}
| Collection | Names after as |
|---|---|
| Array, slice, set, deque | value, index |
| Map, HashMap | value, key, index |
| String | byte, index |
| Array of tuples | one name per member, then index |
skip n starts after the first n items; the index still counts from 0 at the first item. Removing entries from a map or set while looping over it is safe.
Count with a range#
each start .. count walks count numbers starting at start. The second number is a length, not an end:
fn main() {
each 0 .. 3 as i : print(i) // 012
println("")
each 5 .. 3 as number, position {
print("%number@%position ") // 5@0 6@1 7@2
}
println("")
each 0 .. 3 : print("x") // xxx
println("")
}
Ranges mean the same everywhere: items[5 .. 3] takes three items starting at index 5.
Break and continue#
fn main() {
let grid = Array[Array[int]]{ .{ 1, 2 }, .{ 3, 4 } }
each grid as row {
each row as cell {
if cell == 2 : continue
if cell == 4 : break 2
print(cell)
}
}
println("") // 13
}
break leaves the loop, continue starts its next round. A number leaves that many loops: break 2 also ends the outer loop.
Match a set of alternatives#
match compares a value against cases. With : Type it produces a value:
enum Status { queued, running, finished }
fn label(status: Status) String {
return match status : String {
.queued => "Waiting"
.running => "In progress"
.finished => "Done"
}
}
fn main() {
println(label(.running)) // In progress
let status = Status.finished
match status {
.finished => println("All done")
default => println("Still busy")
}
}
Every case of a value match must produce the type, or leave with return, throw, break, continue or panic. Covering every enum item (or true and false) makes default unnecessary.
Match patterns#
fn size(n: int) String {
return match n : String {
0 => "none"
1, 2, 3 => "few" // several values
4 .. 6 => "some" // range: 4 to 9
_ if n < 0 => "negative" // any value, with a guard
default => "many"
}
}
fn command(input: String) String {
return match input : String {
"start", "run" => "starting"
"stop" => "stopping"
default => "unknown"
}
}
fn main() {
println(size(2)) // few
println(size(-5)) // negative
println(command("run")) // starting
}
| Pattern | Matches |
|---|---|
5, "text", 'a', .item |
One value |
1, 2, 3 |
Any listed value |
'a' .. 26 |
An integer range (start, count) |
pattern if condition |
Only when the guard is true |
_ |
Anything |
String as s |
A type of a tagged union, bound to s |
null |
A missing value |
default |
Everything not matched before |
Compute a value in a block#
<{ ... } runs several statements and return gives the block's value:
fn main() {
let total = <{
let subtotal = 40
let delivery = 5
return subtotal + delivery
}
println(total) // 45
let port = "abc".to(int) !? <{
println("Invalid port, using 8080")
return 8080
}
println(port)
}
This is handy as an error fallback that needs more than one step.

