Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Switch

Switch statements and expressions do not work yet.

Switch statements are not yet implemented in the compiler, at all. You will get compile errors front and center if you try to use them.

Switch Statements

Switch statements are actually pretty easy to understand once you grasped if chains. switch statements are primarily used for pattern-matching purposes or for cases where you have a limited selection of possible values, like with enums, where you only have the possibilities of each tag.

Let's actually re-write the same example from the previous example, but without the if chain we now use a switch statement:

use Core.print

enum ComparisonResult:
    BIGGER, SMALLER, EQUAL;

def compare(i32 x, i32 y) -> ComparisonResult:
    if x > y:
        return ComparisonResult.BIGGER;
    else if y < x:
        return ComparisonResult.SMALLER;
    else:
        return ComparisonResult.EQUAL;

def main():
    ComparisonResult result = compare(10, 5);
    switch result:
        ComparisonResult.BIGGER:
            print("is bigger\n");
        ComparisonResult.SMALLER:
            print("is smaller\n");
        ComparisonResult.EQUAL:
            print("is equal\n");

This program will print this line to the console:

is bigger

If you know switch statements from other languages you may wonder "where are the case and the break keywords?". Flint does not have such keywords, at least in this context (break still exists for loops, just like continue). In most languages like C, switch statements undergo a default fallthrough and you must manually opt out of the fallthrough behaviour, which is extremely error prone. But if you dont know what fallthrough is, lets discuss this first.

Fallthrough

Fallthrough is the act of executing multiple switch branches after another. We best look at this example from C:

#include <stdio.h>

typedef enum { VAL1, VAL2, VAL3 } MyEnum;

int main() {
    MyEnum e = VAL1; // In C you dont need to write MyEnum.VAL1
    switch (e) {
        case VAL1:
            printf("is val1\n");
        case VAL2:
            printf("is val2\n");
        case VAL3:
            printf("is val3\n");
    }
}

This program will print these lines to the console:

is val1
is val2
is val3

And now you might think...what? Why does this happen? This is fallthrough in action. The first case that got matched is actually the case VAL1 line. Falltrough means that "the execution falls through (to the next branch)". So, after the case VAL1 branch, the case VAL2 branch got executed and the case VAL3 branch afterwards. If we want the intuitively expected behaviour, where each branch is executed with no fallthrough, we would need to add the break keyword to every single branch:

#include <stdio.h>

typedef enum { VAL1, VAL2, VAL3 } MyEnum;

int main() {
    MyEnum e = VAL1; // In C you dont need to write MyEnum.VAL1
    switch (e) {
        case VAL1:
            printf("is val1\n");
            break;
        case VAL2:
            printf("is val2\n");
            break;
        case VAL3:
            printf("is val3\n");
            break;
    }
}

This program will print this line to the console:

is val1

And this is the behaviour that Flint has by default. In Flint, falltrough is not opt-out but rather opt-in. We don't actually have a keyword for this but rather an annotation. Here is an example of it:

use Core.print

enum MyEnum:
    VAL1, VAL2, VAL3;

def main():
    MyEnum e = MyEnum.VAL1;
    switch e:
        #fallthrough
        MyEnum.VAL1:
            print("is val1\n");
        MyEnum.VAL2:
            print("is val2\n");
        MyEnum.VAL3:
            print("is val3\n");

This program will print these lines to the console:

is val1
is val2

It actually is not clear yet if there will exist an explicit keyword for the fallthrough. We did not want to use the continue keyword for this purpose, because what happens if you have a switch statement within a for loop, would the for loop continue or would the switch branch fall through? Flint tries to avoid ambiguity at all cost at all places, so we are pretty careful with its design.

Switch Expressions

A switch, however, can not only exist as a statement but as an expression too. Instead of executing an arbitrary block of code, each switch branch needs to be an explicit expression now, marked with the arrow -> syntax. Have a look:

use Core.print

enum MyEnum:
    VAL1, VAL2, VAL3;

def main():
    MyEnum e = MyEnum.VAL1;
    i32 result = switch e:
        MyEnum.VAL1 -> 1;
        MyEnum.VAL2 -> 2;
        MyEnum.VAL3 -> 4;
    print("result = {result}\n");

This program will print this line to the console:

result = 1

For switch expressions, there does not exist such thing as a fallthrough, because there are no code blocks executed but only single expressions. Yes, a literal like 1 is an expression too, but you could write function calls or even another nested switch expression on the place of that literal.