# Control flow

> if, elif and else, conditional expressions, chained comparisons, for loops over ranges, arrays and strings, while, break, continue, pass, and the logical operators.

Source: https://docs.brinelang.org/language/control-flow/

Control flow reads like Python: `if`/`elif`/`else`, `for x in ...`, `while`, `break`, `continue` and `pass`, with `and`, `or` and `not`. The differences: conditions must be `bool` (no “truthy” values), and a `for` loop over an array walks a snapshot of it.

## if, elif and else

[Section titled “if, elif and else”](#if-elif-and-else)

```brine
fn classify(x: int) -> str:
    if x < 0:
        return "negative"
    elif x == 0:
        return "zero"
    else:
        return "positive"


fn main():
    for x in [-3, 0, 8]:
        let parity = "even" if x % 2 == 0 else "odd"
        print(x, classify(x), parity)
```

Output

```text
-3 negative odd
0 zero even
8 positive even
```

`a if cond else b` is the conditional expression. Both branches must have the same type.

A condition must be a `bool`. An integer, a string or an array isn’t true or false on its own; compare explicitly.

```brine
fn main():
    let items = [1, 2]
    if len(items):
        print("not empty")
```

```text
error[E0307]: condition must be a `bool`, found `int`
 --> main.br:3:8
  |
3 |     if len(items):
  |        ^^^^^^^^^^ not a bool
  |
  = help: compare explicitly, e.g. `x != 0`
  = run `brine explain E0307` for more about this error
error: could not compile due to 1 error
```

Habits from other languages get a direct fix. `else if` is [E0114](/reference/errors/#e0114) (write `elif`), and `&&`, `||` and `!` are [E0105](/reference/errors/#e0105) (write `and`, `or`, `not`):

```brine
fn main():
    let x = 3
    if x > 0 && x < 5:
        print("in range")
```

```text
error[E0105]: `&&` is not an operator in brine
 --> main.br:3:14
  |
3 |     if x > 0 && x < 5:
  |              ^^ use `and`
  = run `brine explain E0105` for more about this error
error: could not compile due to 1 error
```

## Comparisons and logic

[Section titled “Comparisons and logic”](#comparisons-and-logic)

Comparisons chain, as in Python: `0 <= i < n` means `0 <= i and i < n`. `and` and `or` short-circuit: the right side runs only when it’s needed.

```brine
fn main():
    let xs = [4, 8, 15]
    let i = 2
    print(0 <= i < len(xs))
    print(1 < 2 < 3 > 0)
    print(len(xs) > 0 and xs[0] == 4)
    let empty: [int] = []
    print(len(empty) > 0 and empty[0] == 4)   # stops before the index
    print(not (i == 2) or i > 1)
```

Output

```text
true
true
true
false
true
```

`==` and `!=` compare values by content on every type (two arrays are equal when their elements are; the same goes for maps, tuples, structs and enums). `<`, `<=`, `>` and `>=` work on numbers and strings.

## for over ranges

[Section titled “for over ranges”](#for-over-ranges)

`a..b` counts from `a` up to `b - 1`; `a..=b` includes `b`. `range(...)` works like Python’s and is the way to count with a step, including downward.

```brine
fn main():
    for i in 0..4:
        print(i, end=" ")
    print()
    for i in 1..=4:
        print(i, end=" ")
    print()
    for i in range(5):
        print(i, end=" ")
    print()
    for i in range(2, 5):
        print(i, end=" ")
    print()
    for i in range(0, 10, 3):
        print(i, end=" ")
    print()
    for i in range(10, 0, -3):
        print(i, end=" ")
    print()
```

Output

```text
0 1 2 3
1 2 3 4
0 1 2 3 4
2 3 4
0 3 6 9
10 7 4 1
```

A range whose end is below its start runs zero times. A `range` step of zero panics. The loop variable has the type of the bounds: `for j in 0..n` with `n: u8` gives a `u8`.

Ranges exist only in `for` loops and comprehensions; they aren’t values:

```brine
fn main():
    let r = 0..5
    print(r)
```

```text
error[E0312]: ranges can only be used in `for` loops and comprehensions
 --> main.br:2:13
  |
2 |     let r = 0..5
  |             ^^^^ range used as a value
  |
  = help: to make an array, write `[i for i in 0..n]`
  = run `brine explain E0312` for more about this error
error: could not compile due to 1 error
```

## for over arrays, strings and maps

[Section titled “for over arrays, strings and maps”](#for-over-arrays-strings-and-maps)

```brine
fn main():
    var xs = [1, 2, 3]
    for x in xs:                 # loops over xs as it was here
        xs.push(x * 10)
    print(xs)
    for i, name in enumerate(["ann", "bob"]):
        print(i, name)
    for c in "héllo":
        print(c, end="|")
    print()
```

Output

```text
[1, 2, 3, 10, 20, 30]
0 ann
1 bob
h|é|l|l|o|
```

* `for x in xs` walks a **snapshot** of `xs` taken when the loop starts. The body may change `xs` (push, pop, assign) without affecting the iteration, and it never runs past the end.
* `enumerate(xs)` gives `(index, element)` tuples.
* `for c in s` walks the UTF-8 **characters** of a string, each as a one-character `str`. (Indexing a string counts bytes; see [strings](/language/strings/).)
* `for k in m` walks a map’s keys in insertion order, also over a snapshot; see [maps](/language/maps/).

## Tuple unpacking in for

[Section titled “Tuple unpacking in for”](#tuple-unpacking-in-for)

When the elements are tuples, name their parts in the loop header. Patterns nest, and they work the same in comprehensions.

```brine
fn main():
    let pairs = [("x", 1), ("y", 2)]
    for name, value in pairs:
        print(name, value)
    for i, (name, value) in enumerate(pairs):
        print(i, name, value)
    let flat = [f"{k}={v}" for k, v in pairs]
    print(flat)
```

Output

```text
x 1
y 2
0 x 1
1 y 2
["x=1", "y=2"]
```

Loop variables take annotations too: `for i: int in 0..n:`, or `for name: str, age: int in ages.items():`.

## while, break, continue and pass

[Section titled “while, break, continue and pass”](#while-break-continue-and-pass)

```brine
fn main():
    var k = 0
    while k < 10:
        k += 1
        if k % 2 == 0:
            continue             # skip even numbers
        if k > 7:
            break                # leave the loop
        print(k, end=" ")
    print()
    for i in 0..3:
        if i == 1:
            pass                 # nothing to do here (yet)
        else:
            print(i)
```

Output

```text
1 3 5 7
0
2
```

`break` and `continue` apply to the innermost loop. `pass` is a statement that does nothing, for a block that must not be empty.

Loops that the [prover](/proofs/contracts/) should reason about can carry an `invariant`; parallel loops are written `par for` (see [parallelism](/parallelism/overview/)).

## Summary

[Section titled “Summary”](#summary)

* `if` / `elif` / `else`; conditions must be `bool`.
* `a if cond else b` is an expression.
* Comparisons chain (`0 <= i < n`); `and` / `or` short-circuit; `not` negates.
* `for i in a..b` (exclusive), `a..=b` (inclusive), `range(stop)`, `range(start, stop)`, `range(start, stop, step)`.
* `for x in xs` iterates over a snapshot; `enumerate` adds indexes; `for c in s` walks characters; tuple elements unpack in the loop header.
* `while`, `break`, `continue`, `pass` work as in Python.
