Beginner

Control Flow in Mojo

Learn control flow in Mojo - conditionals, while and for loops, loop control, and ternary expressions with Docker-ready examples

Control flow determines the order in which your program’s statements run—which branches execute, how many times a block repeats, and when to stop early. Without it, a program is just a straight line of instructions. Control flow is what lets code make decisions and react to data.

If you’ve written Python, Mojo’s control flow will feel immediately familiar. As a Python superset, Mojo uses the same if/elif/else conditionals, the same while and for loops, the same break and continue keywords, and the same indentation-based blocks. The difference is what happens underneath: in fn functions and compiled paths, these constructs lower through MLIR to optimized native machine code rather than running on an interpreter, so the friendly syntax carries no runtime tax.

In this tutorial you’ll learn how to branch with conditionals, repeat work with while and for loops, control loops with break and continue, and write compact decisions with ternary expressions. Every example runs as-is with the Mojo Docker image.

Conditionals: if, elif, else

Conditionals choose a branch based on boolean tests. Mojo evaluates each condition top to bottom and runs the first block whose condition is true; else catches everything else.

Create a file named control_flow_conditionals.mojo:

def main():
    temperature = 72

    if temperature > 85:
        print("It's hot outside")
    elif temperature > 60:
        print("It's a pleasant day")
    elif temperature > 32:
        print("It's chilly")
    else:
        print("It's freezing")

    # Combine conditions with boolean operators
    is_weekend = True
    is_sunny = True
    if is_weekend and is_sunny:
        print("Time for a hike!")

    # Negation and 'or'
    has_umbrella = False
    if not has_umbrella or is_sunny:
        print("No need to worry about rain")

Mojo uses the keywords and, or, and not for boolean logic—not the &&/||/! symbols of C-family languages. Conditions must evaluate to a boolean, and the colon plus indentation defines each block exactly as in Python.

Loops: while and for

A while loop repeats as long as its condition holds. A for loop iterates over a sequence—most commonly the values produced by range().

Create a file named control_flow_loops.mojo:

def main():
    # While loop: repeat until a condition becomes false
    countdown = 5
    while countdown > 0:
        print("T-minus", countdown)
        countdown -= 1
    print("Liftoff!")

    # For loop over a range (start inclusive, end exclusive)
    print("Squares:")
    for i in range(1, 6):
        print(i, "squared is", i * i)

    # Range with a step value
    print("Even numbers:")
    for n in range(0, 10, 2):
        print(n)

range(1, 6) yields 1, 2, 3, 4, 5—the start is inclusive and the end is exclusive. With three arguments, range(0, 10, 2) adds a step, producing 0, 2, 4, 6, 8. The loop variable (i, n) is an Int here, and arithmetic like i * i compiles to native integer operations.

Loop Control: break, continue, and FizzBuzz

break exits a loop immediately, and continue skips to the next iteration. Combined with conditionals, they express most real-world iteration logic—including the classic FizzBuzz exercise.

Create a file named control_flow_advanced.mojo:

def main():
    # continue: skip values that don't match
    print("Even numbers under 10:")
    for i in range(10):
        if i % 2 != 0:
            continue
        print(i)

    # break: stop as soon as we find what we want
    print("First multiple of 7:")
    for n in range(1, 100):
        if n % 7 == 0:
            print("Found:", n)
            break

    # FizzBuzz: conditionals + loop working together
    print("FizzBuzz to 15:")
    for num in range(1, 16):
        if num % 15 == 0:
            print("FizzBuzz")
        elif num % 3 == 0:
            print("Fizz")
        elif num % 5 == 0:
            print("Buzz")
        else:
            print(num)

The % modulo operator returns the remainder of a division, making it the natural tool for “is this divisible by N?” checks. Note that the FizzBuzz if chain tests % 15 first: order matters because the first matching branch wins, and a number divisible by 15 is also divisible by both 3 and 5.

Ternary Expressions

When you need a value rather than a block of statements, Mojo supports Python’s conditional expression: value_if_true if condition else value_if_false. It evaluates to one of two values in a single line.

Create a file named control_flow_ternary.mojo:

def main():
    score = 78
    result = "pass" if score >= 60 else "fail"
    print("Result:", result)

    # Ternaries can feed directly into other expressions
    count = 1
    label = "item" if count == 1 else "items"
    print(count, label)

    # Nesting works, but keep it readable
    number = 0
    sign = "positive" if number > 0 else "negative" if number < 0 else "zero"
    print("The number is", sign)

A ternary is an expression—it produces a value—so it fits anywhere a value is expected. This makes it ideal for small either/or choices where a full if/else block would be overkill.

Running with Docker

Run any of the examples with the Mojo Docker image. No local Mojo installation is required.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# Pull the Mojo image
docker pull codearchaeology/mojo:latest

# Run the conditionals example
docker run --rm -v $(pwd):/app -w /app codearchaeology/mojo:latest mojo control_flow_conditionals.mojo

# Run the loops example
docker run --rm -v $(pwd):/app -w /app codearchaeology/mojo:latest mojo control_flow_loops.mojo

# Run the loop control example
docker run --rm -v $(pwd):/app -w /app codearchaeology/mojo:latest mojo control_flow_advanced.mojo

# Run the ternary example
docker run --rm -v $(pwd):/app -w /app codearchaeology/mojo:latest mojo control_flow_ternary.mojo

Expected Output

Running control_flow_conditionals.mojo:

It's a pleasant day
Time for a hike!
No need to worry about rain

Running control_flow_loops.mojo:

T-minus 5
T-minus 4
T-minus 3
T-minus 2
T-minus 1
Liftoff!
Squares:
1 squared is 1
2 squared is 4
3 squared is 9
4 squared is 16
5 squared is 25
Even numbers:
0
2
4
6
8

Running control_flow_advanced.mojo:

Even numbers under 10:
0
2
4
6
8
First multiple of 7:
Found: 7
FizzBuzz to 15:
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz

Running control_flow_ternary.mojo:

Result: pass
1 item
The number is zero

A Note on match

Many languages offer a switch or match statement for multi-way branching. Mojo is still pre-1.0 and does not yet provide a stable structural match statement, so the idiomatic way to handle multiple cases today is an if/elif/else chain—as shown in the FizzBuzz example above. When you have many discrete cases, chaining elif branches keeps the logic clear and runs efficiently in compiled code.

Key Concepts

  • Python-familiar syntaxif/elif/else, while, and for work exactly as they do in Python, including indentation-based blocks
  • Word-based boolean operators — Mojo uses and, or, and not rather than &&, ||, and !
  • range() is exclusive at the endrange(1, 6) produces 1 through 5; a third argument sets the step
  • break and continuebreak exits a loop early; continue skips to the next iteration
  • First match wins — in an if/elif chain, ordering matters (test the most specific condition, like % 15, first)
  • Ternary expressionsa if cond else b produces a value inline, ideal for small two-way choices
  • No match yet — use if/elif/else chains for multi-way branching in current Mojo
  • Zero-overhead in compiled paths — control flow lowers through MLIR to native machine code, so the readable syntax carries no interpreter cost

Running Today

All examples can be run using Docker:

docker pull codearchaeology/mojo:latest
Last updated:

Comments

Loading comments...

Leave a Comment

2000 characters remaining