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.
| |
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 syntax —
if/elif/else,while, andforwork exactly as they do in Python, including indentation-based blocks - Word-based boolean operators — Mojo uses
and,or, andnotrather than&&,||, and! range()is exclusive at the end —range(1, 6)produces1through5; a third argument sets the stepbreakandcontinue—breakexits a loop early;continueskips to the next iteration- First match wins — in an
if/elifchain, ordering matters (test the most specific condition, like% 15, first) - Ternary expressions —
a if cond else bproduces a value inline, ideal for small two-way choices - No
matchyet — useif/elif/elsechains 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
Comments
Loading comments...
Leave a Comment