Control Flow

On this page 24

Home provides familiar control flow constructs with some powerful additions like pattern matching and expression-oriented design.

If/Else Statements

Basic conditionals require parentheses around the condition:

if (x > 5) {
  print("x is big")
}

if (age >= 18) {
  print("Adult")
} else {
  print("Minor")
}

if (score >= 90) {
  print("A")
} else if (score >= 80) {
  print("B")
} else if (score >= 70) {
  print("C")
} else {
  print("F")
}

If Expressions

If statements can be used as expressions that return values:

let status = if (code == 200) { "ok" } else { "error" }

let grade = if (score >= 90) {
  "A"
} else if (score >= 80) {
  "B"
} else if (score >= 70) {
  "C"
} else {
  "F"
}

// In function returns
fn abs(x: int): int {
  return if (x >= 0) { x } else { -x }
}

While Loops

Execute while a condition is true:

let mut count = 0

while (count < 5) {
  print(count)
  count = count + 1
}

Break and Continue

Control loop execution:

let mut i = 0
while (true) {
  i = i + 1

  if (i == 3) {
    continue  // Skip to next iteration
  }

  if (i > 5) {
    break  // Exit the loop
  }

  print(i)  // Prints: 1, 2, 4, 5
}

For Loops

Iterate over ranges and collections:

Range Iteration

// Iterate 0 to 9
for (i in 0..10) {
  print(i)
}

// Iterate 1 to 10 (inclusive)
for (i in 1..=10) {
  print(i)
}

// Simple count iteration
for (i in 10) {
  print(i)  // 0 to 9
}

Collection Iteration

let items = ["apple", "banana", "cherry"]

for (item in items) {
  print(item)
}

// With index
for (index, item in items) {
  print("{index}: {item}")
}

Step Iteration

// Every other number
for (i in (0..10).step(2)) {
  print(i)  // 0, 2, 4, 6, 8
}

// Countdown
for (i in (10..0).step(-1)) {
  print(i)  // 10, 9, 8, ..., 1
}

Match Expressions

Powerful pattern matching:

Basic Matching

let x = 2

match x {
  1 => print("one"),
  2 => print("two"),
  3 => print("three"),
  _ => print("other")  // Default case
}

Match as Expression

let name = match x {
  1 => "one",
  2 => "two",
  3 => "three",
  _ => "other"
}

Matching Enums

enum Color {
  Red,
  Green,
  Blue,
  Custom(r: int, g: int, b: int)
}

let color = Color.Custom(255, 128, 0)

match color {
  Color.Red => print("red"),
  Color.Green => print("green"),
  Color.Blue => print("blue"),
  Color.Custom(r, g, b) => print("rgb({r}, {g}, {b})")
}

Matching Result Types

match read_file("config.home") {
  Ok(content) => process(content),
  Err(e) => print("Failed: {e}")
}

Guards

Add conditions to match arms:

match x {
  n if n < 0 => print("negative"),
  n if n == 0 => print("zero"),
  n if n > 0 => print("positive"),
  _ => unreachable!()
}

Multiple Patterns

Match multiple values in one arm:

match x {
  1 | 2 | 3 => print("small"),
  4 | 5 | 6 => print("medium"),
  7 | 8 | 9 => print("large"),
  _ => print("other")
}

Range Patterns

match score {
  0..=59 => print("F"),
  60..=69 => print("D"),
  70..=79 => print("C"),
  80..=89 => print("B"),
  90..=100 => print("A"),
  _ => print("Invalid score")
}

Destructuring in Patterns

struct Point { x: int, y: int }

let point = Point { x: 10, y: 20 }

match point {
  Point { x: 0, y: 0 } => print("origin"),
  Point { x: 0, y } => print("on y-axis at {y}"),
  Point { x, y: 0 } => print("on x-axis at {x}"),
  Point { x, y } => print("at ({x}, {y})")
}

Early Returns

Use return to exit a function early:

fn find_user(id: int): Option<User> {
  if (id <= 0) {
    return None
  }

  let user = database.find(id)
  if (user.is_none()) {
    return None
  }

  return Some(user.unwrap())
}

Guard Clauses

A common pattern for early validation:

fn process_order(order: Order): Result<Receipt, Error> {
  // Guard clauses - check for invalid states first
  if (order.items.is_empty()) {
    return Err(Error.new("Order has no items"))
  }

  if (order.customer.is_none()) {
    return Err(Error.new("No customer specified"))
  }

  if (!order.is_valid()) {
    return Err(Error.new("Invalid order"))
  }

  // Main logic
  let total = calculate_total(order)
  let receipt = generate_receipt(order, total)
  return Ok(receipt)
}

Loop Labels

Name loops to break or continue outer loops:

'outer: for (i in 0..10) {
  for (j in 0..10) {
    if (i _ j > 50) {
      break 'outer  // Breaks the outer loop
    }
    print("{i} _ {j} = {i * j}")
  }
}

Ternary-Style Expressions

Home uses if expressions instead of ternary operators:

// Instead of: condition ? true_value : false_value
let value = if (condition) { true_value } else { false_value }

// Compact form for simple cases
let max = if (a > b) { a } else { b }

Boolean Operators

Short-circuit evaluation:

// && stops if left side is false
if (user != null && user.is_active()) {
  process(user)
}

// || stops if left side is true
let name = user?.name || "Anonymous"

Exhaustive Matching

The compiler ensures all cases are handled:

enum Status {
  Pending,
  Active,
  Completed,
  Failed
}

// This must handle all variants
fn describe(status: Status): string {
  match status {
    Status.Pending => "Waiting to start",
    Status.Active => "In progress",
    Status.Completed => "Done",
    Status.Failed => "Error occurred"
    // No _ needed - all cases covered
  }
}

Next Steps

Released under the MIT License.