Is every if statement replaceable with match

Hello

Trying to replace following snippet with if statement with match in rust playground , some hint would be helpful:

fn main() {
    let my_number = 5;
    
    
    if my_number % 2 == 1 && my_number > 0 { // % 2 means the number that remains after diving by two
        println!("It's a positive odd number");
    } else if my_number == 6 {
        println!("It's six")
    } else {
        println!("It's a different number")
    }
    

}

The following snippet worked :slightly_smiling_face:
Do you consider it a good practice ?

    match my_number {
        my_number if my_number % 2 == 1 && my_number > 0 => println!("It's a positive odd number"),
        my_number if my_number == 6 => println!("It's six"),
        _ => println!("It's a different number")
    }

Note that the temporary scopes in match expressions are slightly different. This can be surprising:

use std::cell::RefCell;

fn main() {
    let x = RefCell::new(0);
    // this works:
    if *x.borrow() < 5 {
        *x.borrow_mut() += 1;
    }
    // this will panic:
    match *x.borrow() < 5 {
        true => *x.borrow_mut() += 1,
        _ => (),
    };
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
    Finished dev [unoptimized + debuginfo] target(s) in 1.62s
     Running `target/debug/playground`
thread 'main' panicked at 'already borrowed: BorrowMutError', src/main.rs:11:20
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

2 Likes

How about writing:

fn main() {
    let my_number = 5;
    match my_number {
        n if n % 2 == 1 && n > 0 => println!("It's a positive odd number"),
        6 => println!("It's six"),
        _ => println!("It's a different number")
    }
}

(Playground)

The (revised) match statement seems more concise, and I feel like it would be idiomatic (in my opionion). But the if is fine too, I guess.

@jbe - so n is just an alias to my_number here

Yes, exactly. It can be any name. I feel like using a short name like n is best here.

In theory, every if statement can be translated into a match statement (e.g. match cond { true => true_value, false => false_value }), but... just because you can do something doesn't mean you should.

If you are just changing control flow based on a predicate (e.g. my_number % 2 == 1 && my_number > 0) then the if statement will almost always be more readable and require less code. Where match really shows off its value is more complex control flow where you want to bind to a struct field or an enum variant and do custom logic in one statement.

For example,

struct Person { name: String, age: u32 }

match db.lookup_person_by_name("Michael") {
  Some(Person { age, .. } if age % 2 == 1 => println!("Michael has an odd age"),
  Some(Person { .. }) => println!("Michael is in the database"),
  None => println!("There is no Michael in the database"),
}
2 Likes

I generally agree, but note that the revised match avoids repeating my_number by binding the value to the shorter name n. This is why I think the match has some advantages in that case.

Hers some creative use of match featuring matching on tuples and matching against ranges; mostly for educational purposes, I'm not sure if I'd actually want to write it this way

fn main() {
    let my_number = 5;
    match (my_number, my_number % 2) {
        (1.., 1) => println!("It's a positive odd number"),
        (6, _) => println!("It's six"),
        _ => println!("It's a different number"),
    }
}
3 Likes

I tried this, but it doesn't work with stable Rust:

#![feature(if_let_guard)]

fn main() {
    let my_number = 5;
    match my_number {
        n if let (1.., 1) = (n, n % 2) => println!("It's a positive odd number"),
        6 => println!("It's six"),
        _ => println!("It's a different number"),
    }
}

(Playground)

Since only a bool is needed, you could go with matches!(…) to write such a guard.

Oh right, it's reversing the order but works fine:

fn main() {
    let my_number = 5;
    match my_number {
        n if matches!((n, n % 2), (1.., 1)) => println!("It's a positive odd number"),
        6 => println!("It's six"),
        _ => println!("It's a different number"),
    }
}

(Playground)

Actually I saw matches! in the error message of stable Rust, but didn't try it, as it felt like not really exploiting the language construct. But I guess matches! is a pretty basic thing, plus it's in std.

Compared to what? It expands to a literal match and thus allows you to do things like pattern match on non-PartialEq-implementing enums and so on.

1 Like

It was just a feeling :sweat_smile:. But I see now that it interally even uses match.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.