As always, I have some strong opinions on things I probably shouldn’t have opinions on. This time I want to tell you why I think if-else chains are syntactically harmful.

I think that the syntax for if-else chains that are used in most imperative languages, including Rust, is bad.

if something.something() {
    do_something()
} else if let Some(x) = get_x() {
    use_x(x);
} else if mutliline {
    a();
    b();
    c();
} else {
    scream("aaaaaaaaaaaaaaaaaaaaaaa")?;
}

It’s hard to read:

  • conditions are mixed with if/else/let keyword-noise

    🐸

    [else] if let in Rust allows pattern-matching behaviour in if statements

  • conditions are indented more than expressions

  • conditions are indented inconsistently between the first branch (if) and the following ones (else if)

  • one-liners take a lot of lines

  • etc.

🐸

Singular ifs are fine I guess, it’s the else if chains that make all of this particularly bad

I think that a much better way to model the same thing is when from Kotlin or pattern guards/multi-way-if from Haskell.

Kotlin and Haskell examples

when {
    x.isOdd() -> print("x is odd")
    y.isEven() -> print("y is even")
    else -> print("x+y is odd")
}
case () of _
    | condition1 -> expr1
    | condition2 -> expr2
    | condition3 -> expr3
    | otherwise  -> default

if | condition1 -> expr1
    | condition2 -> expr2
    | condition3 -> expr3
    | otherwise  -> default

In Rust this can be simulated via match on unit with pattern guards (Boxy style):

match () {
    _ if something.something() => do_something(),
    _ if let Some(x) = get_x() => use_x(x),
    _ if multiline => {
        a();
        b();
        c();
    }
    _ => scream("aaaaaaaaaaaaaaaaaaaaaaa")?,
}

But this is still messy, if let guards are unstable and the repeated ifs are still there.

This is why I created kiam (#shamelessplug?) — a crate that provides a simple macro that makes these chains a lot nicer:

when! {
    something.something() => do_something(),
    let Some(x) = get_x() => use_x(x),
    multiline => {
        a();
        b();
        c();
    }
    _ => scream("aaaaaaaaaaaaaaaaaaaaaaa")?,
}
🐸

I’ve explained kiam with memes on twitter dot com

🐸

One of the only downsides of when! is IDE and tooling (rustfmt) support (or rather the absence of it). I wish this was part of the language or std…

Soooo yeah, there isn’t much to add here. If-else chains are bad for humans, macros are bad for IDEs, I have a cool crate.

Bye.