Control Flow
Conditional Statements
Section titled “Conditional Statements”If-Else
Section titled “If-Else”use go:fmt
let temperature = 22
if temperature > 30 { fmt::Println("It's hot!")} else if temperature < 10 { fmt::Println("It's cold!")} else { fmt::Println("Nice weather!")}Conditions must be boolean expressions. There are no implicit truthy/falsy coercions. Comparison operators include ==, !=, <, <=, >, and >=; combine boolean expressions with and, or, and not.
For Loops
Section titled “For Loops”Iterating Over Collections
Section titled “Iterating Over Collections”use go:fmt
let fruits = ["apple", "banana", "cherry"]for fruit, index in fruits { fmt::Println("{index}: {fruit}")}The index cursor can be omitted in list loops
use go:fmt
let fruits = ["apple", "banana", "cherry"]for fruit in fruits { fmt::Println(fruit)}Iterating Over Maps
Section titled “Iterating Over Maps”use go:fmt
let scores: [Str:Int] = ["Alice": 95, "Bob": 87, "Carol": 92]for name, score in scores { fmt::Println("{name} scored {score.to_str()}")}Numeric Ranges
Section titled “Numeric Ranges”use go:fmt
// Inclusive rangefor i in 1..10 { fmt::Println(i)}To iterate with a step other than 1, use a C-style loop:
use go:fmt
for mut i = 0; i <= 100; i =+ 10 { fmt::Println(i) // Prints 0, 10, 20, ..., 100}C-Style For Loop
Section titled “C-Style For Loop”use go:fmt
for mut i = 0; i <= 5; i =+ 1 { fmt::Println("Count: {i}")}While Loops
Section titled “While Loops”use go:fmt
mut count = 0while count < 10 { fmt::Println("Count is {count}") count =+ 1}Match Expressions
Section titled “Match Expressions”Match expressions are similar to switch expressions in most languages. They come in two forms: value matching (with a subject) and conditional matching (without a subject).
Conditional Matching
Section titled “Conditional Matching”Match expressions can be used without a subject to create clean conditional logic as an alternative to if-else chains:
let score = 85
let grade = match { score >= 90 => "A", score >= 80 => "B", score >= 70 => "C", score >= 60 => "D", _ => "F",}This is equivalent to, but more concise than, the if-else chain:
fn grade(score: Int) Str { if score >= 90 { "A" } else if score >= 80 { "B" } else if score >= 70 { "C" } else if score >= 60 { "D" } else { "F" }}Note that if blocks produce a value only as the final expression of a function body; they cannot be assigned directly with let x = if .... Conditional match is the expression form.
Conditional match expressions evaluate conditions in order and execute the first matching case. A catch-all case (_) is required to ensure the expression always returns a value.
Complex Conditions
Section titled “Complex Conditions”You can use any boolean expressions as conditions:
let age = 30let has_license = truelet has_insurance = true
let status = match { age < 16 => "Too young to drive", not has_license => "Need to get a license", not has_insurance => "Need insurance", age >= 65 => "Senior driver", _ => "Ready to drive",}
let temperature = 75let sunny = truelet weekend = false
let activity = match { temperature > 80 and sunny => "Go to the beach", temperature > 70 and weekend => "Have a picnic", temperature < 50 => "Stay inside and read", _ => "Go for a walk",}Integer Matching
Section titled “Integer Matching”When ranges overlap, the first match wins:
let score = 85
let grade = match score { 0 => "How?", 1..59 => "F", 60..69 => "D", 70..79 => "C", 80..89 => "B", 90..100 => "A", _ => "Invalid score",}Boolean Matching
Section titled “Boolean Matching”let is_valid = true
let response = match is_valid { true => "Proceed", false => "Error: invalid input",}Enum Matching
Section titled “Enum Matching”enum Status { active, inactive, pending,}
let user_status = Status::active
let message = match user_status { Status::active => "Welcome back!", Status::inactive => "Please reactivate account", Status::pending => "Account under review",}Matching on Type Unions
Section titled “Matching on Type Unions”Use match expressions to handle different types in a union:
use go:fmt
type Content = Str | Int | Bool
fn describe(value: Content) Str { match value { Str => "Text: {it}", Int => "Number: {it.to_str()}", Bool => "Flag: {it.to_str()}", }}
let items: [Content] = ["hello", 42, true]for item in items { fmt::Println(describe(item))}Pattern Matching Order
Section titled “Pattern Matching Order”Patterns are evaluated in the order they appear. More specific patterns should come before general ones.
When to Use Match vs If-Else
Section titled “When to Use Match vs If-Else”Use conditional match expressions when:
- You need to return a value based on conditions
- You want cleaner, more functional code
- The logic is pure (no side effects)
Use if statements when:
- You need to perform side effects (like
break,panic, or mutations) - You’re doing control flow within loops
- You’re executing statements rather than returning values
struct User {}
impl User { fn is_admin() Bool { true } fn is_member() Bool { true }}
fn should_skip(item: Int) Bool { false }fn process(item: Int) {}
let user = User{}let items = [1, 2, 3]
// Good: conditional match for valueslet message = match { user.is_admin() => "Admin access granted", user.is_member() => "Member access granted", _ => "Access denied",}
// Good: if statement for control flowfor item in items { // break is a side effect - can't use match here if should_skip(item) { break } process(item)}Loop Control
Section titled “Loop Control”Ard supports the break keyword for early termination of loops.
fn should_skip(item: Int) Bool { false }fn process(item: Int) {}
let items = [1, 2, 3]
for item in items { if should_skip(item) { break } process(item)}Deferred Cleanup
Section titled “Deferred Cleanup”Use defer to schedule cleanup work for the end of the current function, method, closure, or script. Deferred work runs in last-in-first-out order and still runs when try returns early.
fn read_file(path: Str) Str!Str { let file = try open_file(path) defer file.close()
let text = try file.read_all() Result::ok(text)}Both call and block forms are supported:
defer resource.close()
defer { match resource.close() { Result::err(e) => log("close failed: {e}"), Result::ok(_) => (), }}Unlike Go, Ard evaluates the deferred call later by lowering it as a zero-argument closure. If a deferred call captures a mut binding that is reassigned before the function exits, it sees the later value. Bind an explicit snapshot when needed:
let current = resourcedefer current.close()resource = next_resourcetry is not allowed inside deferred work. Handle cleanup results explicitly with match if they matter.