Are there traditional style switch statements in Rust?

The simplest way to do this sort of case analysis is when you know the values of the cases beforehand and don't mind having them in the middle of the code. In this case, a simple match expression is what you want.

fn main() {
    for &input in &[16, 23, 42, 43] {
        match input {
            23 => println!("Input is equal to a"),
            16 => println!("Input is equal to b"),
            43 => println!("Input is equal to c"),
            _ => println!("Input does not equal any value"),
        }
    }
}

(playground link)

If your a b and c are compile-time constants (either a known value or generated with const functions), then you can still directly match on them.

const A: i32 = 23;
const B: i32 = 16;
const C: i32 = generate_c();

const fn generate_c() -> i32 {
    A + B + 4
}

fn main() {
    for &input in &[16, 23, 42, 43] {
        match input {
            A => println!("Input is equal to a"),
            B => println!("Input is equal to b"),
            C => println!("Input is equal to c"),
            _ => println!("Input does not equal any value"),
        }
    }
}

(playground link)

However, if you try this with non-constant variables, you'll get weird outputs.

fn generate_c(a: i32, b: i32) -> i32 {
    a + b + 4
}

fn main() {
    let a = 23;
    let b = 16;
    let c = generate_c(a, b);

    for &input in &[16, 23, 42, 43] {
        match input {
            a => println!("Input is equal to a"),
            b => println!("Input is equal to b"),
            c => println!("Input is equal to c"),
            _ => println!("Input does not equal any value"),
        }
    }
}

(playground link)

If you run this, the compiler will give you lots of warnings about "unreachable patterns" and the output will be "Input is equal to a" all four times. The problem with this is that the left side of each line in a match statement is not simply an expression, but rather a pattern.

A pattern is an expression like (x, [_, z], Some(_)). It's built up from basic variables (like x and z), underscores (_), all the literal expressions (integers, floats, tuples, arrays) and a few other things.

When Rust runs a match statement like this, it tries to syntactically match the input with the pattern. Basic variables will match anything and the value of that variable is set to whatever it matched for the scope of that branch of the match statement. Underscores (as used in all of the above examples) match anything too, but don't bind any variables.

With the const version above, the constants A B and C are replaced with their respective literal values everywhere in the code, so the input is matched on those literal values.

With the variable version, when we match with a b and c, these letters are interpreted as basic variables that match anything. The values of the variables aren't considered at all in the pattern. In the code

let a = 14;
let b = 15;
let c = 16;
let input = 16;
match input {
    a => println!("Input is equal to a"),
    b => println!("Input is equal to b"),
    c => println!("Input is equal to c"),
    _ => println!("Input does not equal any value"),
}

the first branch will always match, giving the input the name a for the scope of the branch.

If you need to match on variables a b and c, you could add a guard to each branch. A guard filters the branch a little more by adding an additional condition for a branch to match. In this case, we match anything and bind it to the variable x, but then check that x is equal to a (and b and c).

fn generate_c(a: i32, b: i32) -> i32 {
    a + b + 4
}

fn main() {
    let a = 23;
    let b = 16;
    let c = generate_c(a, b);

    for &input in &[16, 23, 42, 43] {
        match input {
            x if x == a => println!("Input is equal to a"),
            x if x == b => println!("Input is equal to b"),
            x if x == c => println!("Input is equal to c"),
            _ => println!("Input does not equal any value"),
        }
    }
}

(playground link)

This is a little bit more verbose than the switch/case construction, but I hope it's clear what's going on. At each branch, the variable x is bound to 16, then if that's equal to the variable A (or B or C), then that branch is taken. Otherwise, we try the next branch.


Can I compare a variable to constant values in Rust using a match statement?

I wrote a macro to (almost) do this. It expands to a match with if guards.

switch! { input;
    a => println!("Input is equal to a"),
    b => println!("Input is equal to b"),
    c => println!("Input is equal to c"),
    _ => println!("Input does not equal any value"),
}

Tags:

Rust