Beginner

Control Flow in Nim

Master conditionals, case statements, and loops in Nim - including if-expressions, ranges, iterators, and labeled breaks with Docker-ready examples

Control flow determines the order in which your program executes statements - which branches it takes and how many times it repeats work. Nim gives you a familiar, Python-like set of tools (if, case, for, while) but with a twist that reflects its multi-paradigm nature: most control structures are also expressions that produce values.

Because Nim is statically typed and expression-oriented, an if or case can sit on the right-hand side of an assignment, eliminating the need for a separate ternary operator. Its for loops iterate over iterators rather than manual index counters, and its case statement requires you to handle every possibility - the compiler enforces exhaustiveness, catching missing branches before your program ever runs.

In this tutorial you’ll learn conditional branching with if/elif/else, multi-way dispatch with case, iteration with for and while, the break/continue loop controls (including labeled breaks for nested loops), and how to use control structures as value-producing expressions.

Conditionals: if, elif, else

Nim’s if statement uses indentation rather than braces, and a colon ends each condition. The same construct doubles as an expression.

Create a file named conditionals.nim:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
let temperature = 25

# Standard if / elif / else branching
if temperature < 0:
  echo "Freezing"
elif temperature < 15:
  echo "Cold"
elif temperature < 25:
  echo "Mild"
else:
  echo "Warm"

# 'if' as an expression - no ternary operator needed
let status = if temperature >= 18: "comfortable" else: "uncomfortable"
echo "Status: ", status

Because 25 fails every earlier test, control falls through to the else branch. The second if is used as an expression: it evaluates to a string that is bound to status. This is Nim’s idiomatic replacement for the ?: ternary found in C-like languages.

Multi-Way Branching: case

When you need to dispatch on the value of a single variable, case is cleaner than a chain of elifs. Branches can list multiple values, and the case is checked for exhaustiveness by the compiler - here an else covers any remaining values.

Create a file named case_statement.nim:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
let day = 3

# A single branch can match several values
case day
of 1, 7:
  echo "Weekend"
of 2, 3, 4, 5, 6:
  echo "Weekday"
else:
  echo "Invalid day"

# case works on characters too
let grade = 'B'
case grade
of 'A': echo "Excellent"
of 'B': echo "Good"
of 'C': echo "Average"
else: echo "Needs improvement"

With day equal to 3, the second branch matches and prints Weekday. The character case then matches 'B' and prints Good. Note that branches do not “fall through” as they do in C - each branch is self-contained, with no break required.

Loops: for and while

Nim’s for loop iterates over an iterator - a range, a sequence, or a custom generator. The while loop repeats as long as its condition holds. Built-in iterators like countdown let you walk values in reverse.

Create a file named loops.nim:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# for loop over an inclusive range
for i in 1..5:
  echo "Count: ", i

# for loop over a sequence
let fruits = @["apple", "banana", "cherry"]
for fruit in fruits:
  echo "Fruit: ", fruit

# for loop with both index and value
for index, fruit in fruits:
  echo index, ": ", fruit

# while loop with manual decrement
var n = 3
while n > 0:
  echo "Countdown: ", n
  dec n

# countdown iterator walks values in reverse
for i in countdown(5, 1):
  echo "Down: ", i

The 1..5 syntax builds an inclusive range. Iterating a sequence directly yields each element, while the two-variable form (index, fruit) yields the position alongside the value. The while loop uses dec n to decrement, and countdown(5, 1) produces 5, 4, 3, 2, 1 without any manual bookkeeping.

Loop Control: break, continue, and labeled blocks

continue skips to the next iteration; break exits the loop entirely. To break out of nested loops at once, label a block and break that label directly.

Create a file named loop_control.nim:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# continue skips, break stops
for i in 1..10:
  if i mod 2 == 0:
    continue        # skip even numbers
  if i > 7:
    break           # stop once we pass 7
  echo "Odd: ", i

# labeled break escapes nested loops in one jump
block outer:
  for i in 1..3:
    for j in 1..3:
      if i * j > 4:
        echo "Breaking at ", i, "x", j
        break outer
      echo i, " * ", j, " = ", i * j

In the first loop, even numbers are skipped by continue, and once i exceeds 7 the break ends iteration - so only the odd numbers up to 7 print. In the nested loops, break outer exits both loops the moment the product passes 4, jumping straight out of the labeled block.

Control Flow as Expressions

Nim’s expression-oriented design lets case and if produce values directly. This keeps mapping logic compact and readable.

Create a file named expressions.nim:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# case used as an expression to compute a letter grade
let score = 85
let letter =
  case score div 10
  of 10, 9: "A"
  of 8: "B"
  of 7: "C"
  of 6: "D"
  else: "F"
echo "Grade: ", letter

# combining conditionals - the classic FizzBuzz
for i in 1..15:
  if i mod 15 == 0:
    echo "FizzBuzz"
  elif i mod 3 == 0:
    echo "Fizz"
  elif i mod 5 == 0:
    echo "Buzz"
  else:
    echo i

The case expression evaluates score div 10 (which is 8) and yields "B", assigning it to letter. FizzBuzz then exercises if/elif/else inside a loop, checking divisibility by 15 first so that multiples of both 3 and 5 are handled before the narrower cases.

Running with Docker

You can compile and run every example without installing Nim locally. Each nim c -r command compiles the file to a native binary and runs it immediately.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# Pull the official Nim image
docker pull nimlang/nim:alpine

# Run the conditionals example
docker run --rm -v $(pwd):/app -w /app nimlang/nim:alpine nim c -r conditionals.nim

# Run the case statement example
docker run --rm -v $(pwd):/app -w /app nimlang/nim:alpine nim c -r case_statement.nim

# Run the loops example
docker run --rm -v $(pwd):/app -w /app nimlang/nim:alpine nim c -r loops.nim

# Run the loop control example
docker run --rm -v $(pwd):/app -w /app nimlang/nim:alpine nim c -r loop_control.nim

# Run the expressions example
docker run --rm -v $(pwd):/app -w /app nimlang/nim:alpine nim c -r expressions.nim

Expected Output

Running conditionals.nim:

Warm
Status: comfortable

Running case_statement.nim:

Weekday
Good

Running loops.nim:

Count: 1
Count: 2
Count: 3
Count: 4
Count: 5
Fruit: apple
Fruit: banana
Fruit: cherry
0: apple
1: banana
2: cherry
Countdown: 3
Countdown: 2
Countdown: 1
Down: 5
Down: 4
Down: 3
Down: 2
Down: 1

Running loop_control.nim:

Odd: 1
Odd: 3
Odd: 5
Odd: 7
1 * 1 = 1
1 * 2 = 2
1 * 3 = 3
2 * 1 = 2
2 * 2 = 4
Breaking at 2x3

Running expressions.nim:

Grade: B
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz

Key Concepts

  • Expression-oriented control flow - if and case return values, so Nim has no need for a separate ternary operator; assign their result directly to a variable.
  • Exhaustive case - The compiler requires every possible value to be handled; use an else branch to cover the remainder, and remember branches never fall through.
  • Iterator-based for loops - Loops iterate over ranges (1..5), sequences, and iterators like countdown; the two-variable form yields index and value together.
  • Inclusive ranges - a..b includes both endpoints, unlike the exclusive ranges in some other languages.
  • break and continue - continue skips to the next iteration; break exits the innermost loop.
  • Labeled breaks - Wrap nested loops in a named block and use break <label> to escape all of them in a single jump.
  • Indentation defines blocks - Like Python, Nim uses significant whitespace and a trailing colon rather than braces to delimit control-flow bodies.

Running Today

All examples can be run using Docker:

docker pull nimlang/nim:alpine
Last updated:

Comments

Loading comments...

Leave a Comment

2000 characters remaining