What is the difference between ``if`` and ``match``?

i want to know the key difference between if and match what is the thing that i can do in if or match and i cannot do in the other one ?

1 Like

The main difference is that an if just lets you change the control flow depending on a boolean condition, while a match statement does pattern matching.

That means you can change the control flow based on the state of a particular field or an enum variant, bind things to variables, or arbitrary logic using an if guard.

struct Person {
  name: String,
  address: Option<Address>,
}

struct Address {
  street: String,
  building: BuildingType,
  country: String,
}

impl std::fmt::Display for Address { ... }

enum BuildingType {
  Normal,
  Apartment(u32),
  Lot(u32),
}

match some_person {
    Person {
        name,
        address:
            Some(
                ref address @ Address {
                    building: BuildingType::Apartment(a),
                    ..
                },
            ),
    } if a % 2 == 0 => {
        println!("{name} lives in an even-numbered apartment, {address}");
    }
    Person {
        name,
        address: Some(address),
    } => println!("{name} lives at {address}"),
    Person {
        name,
        address: None,
    } => println!("{name} has no known address"),
}

(playground)

It's possible to implement if using match, but not the other way around.

match condition {
  true => { ... },
  false => { ... },
}

You also have an if-let statement, which is essentially a match statement where we only check one arm and don't support if-guards.

3 Likes

i don't understand what do you mean by if and boolean ? i can use boolean in both if and match i still don't get it

An if expressions' discriminant can only be a Boolean. In a match, you can use any pattern.

that is not a difference because i can add more pattern matching in match adding more else if in if statement does not make a difference

how boolean i don't understand ?

if doesn't let you pattern match and extract the value inside the pattern at the same time. You can do that with if let though, but you won't be able to exhaustively match multiple patterns i.e. the compiler won't check that you covered all cases, and even if you do it will conservately assume you didn't.

1 Like

A Boolean is the logical type for true and false. If you don't know what a Boolean is, then I'm sorry, I can't help you, you'll need to study some basics first.

i know what booleans are i can also mention the type using bool in rust you didn't understand my question,

i mean what you mean by booleans what are you referring to ? if returns anything as match and if x == true this is a simple boolean matching in if statement but what do you mean ? be specific with examples. i hope you understand me i am not talking about booleans themselves i know what they are

if <<SOMETHING_HERE>> {...}

<<SOMETHING_HERE>> -> so called "discriminant", can be only something that is either boolean or "resolves" to boolean (fn returning bool, etc.)

The thing that you can make checks to other things booeans by adding e.g . == something_else is transitioning root question of statement definition/difference out of original scope a bit...

Of course you can add more else ifs further, but it always has to be "something boolean-ish" (i am not familiar with proper terminology so please excuse my pseudo definitions), and they are logically speaking completely new/different/subsequent if statements, not bound to first if in any way...

On the other hand, match accepts other types (event "mixes of types via tuples etc).
This (among other things) from my point of view allows for shortening otherwise cumbersome lengthy chains of if-else statements into neat & concise blocks.

    let greeting = "ahoy";
    let value = 42;

    // matching on "combination" of str/num in single statement,
    // without need to compose complex if else &&/||
    // conditions several times on different rows
    match (greeting, value) {
        ("ahoy", 42) => println!("Ahoy life!"),
        ("ahoy", num) => println!("Bye {num} - i print only 42!"),  
        _ => println!("'Sup..."),
    }

Edit, if i really really had to "define" match on single short sentence, i'd say it's simplified "ugly mess of if/else statements" on steroids. :slight_smile:

Sorry, this is completely incomprehensible. If you are having trouble expressing yourself in English reasonably well, then please seek the help of someone who can translate your question to English.

1 Like

i don't have any problems with english at all you just misunderstood my questions maybe they are too complicated for you to understand but i appreciate your comments :innocent:

A question about if and match is definitely not too complicated for me. I've been using Rust for 6 years and I've been writing code since I was nine. Your question I quoted above is not coherent, I'm sorry but that's not my fault.

Several people have been trying to explain various aspects of what you could possibly be asking, and you were satisfied with none of the answers. Maybe the problem is not with everyone else.

1 Like

thanks for your comments

There are things that are difficult to do without match (mainly because of lifetimes), but there is nothing that really can't be done without match. Hypothetical Rust without match would still be Turing-complete and thus technically capable of doing anything Rust with match can, if you tried hard enough.

That said, using match provides enormous safety and usability benefits that are very much worth learning.

At the surface, match looks similar to switch statement in other languages. However match is much better in many ways. I'll explain some of that below.

Look at this code which uses if but not match:

// Let's suppose you have some Option<T> and you want a value of T
if opt.is_some() {
    let val = opt.unwrap();
    // use val
}

See the is_some and unwrap methods that do essentially the same thing? It's redundant and it wastes everyone's cognitive efforts. With match we can do better:

match opt {
    Some(val) => {
        // use val
    },
    None => {}
}

// Or with `if let`:
if let Some(val) = opt {
    // use val
}

We're no longer repeating ourselves! Instead, we've managed to extract val with the same syntax we've used to check the option's state. While this particular problem could as well have been solved with and_then, match is still great as it scales much better to more complex scenarios.

match is also intelligent. While if blindly obeys whatever the comparison operator of your choice says, match's built-in patterns have more knowledge over the types you're matching.

Let's see it in action:

let mut array = [SomeValue, OtherValue];
take_two_muts(&mut array[0], &mut array[1]);

This code cannot compile because indexing into an array causes the entire array to be borrowed. Without patterns, we would have to resort to unsafe code here. Again, we can do better:

let [mut first, mut second] = array;
take_two_muts(&mut first, &mut second);

Note that first and second are now each their own variables with no relation to each other. This causes the version with pattern usage to compile successfully.

The Reference page on Patterns catalogs the variety of types that are natively understood by the language, as well as other things you can do using patterns.

2 Likes

To give an example:

// NOTE: `MyType` does not implement `PartialEq`
enum MyType {
    A,
    B,
}

fn foo() -> &'static str {
    let x = MyType::A;
    // You cannot do:
    /*
    if x == MyType::A {
        println!("Got A");
    } else if x == MyType::B {
        println!("Got B");
    }
    */
    // We must do:
    if let MyType::A = x {
        "Got A"
    } else if let MyType::B = x {
        "Got B"
    } else {
        // We cannot omit this, but if we used `match` we could.
        unreachable!("should never happen")
    }
}

fn bar() -> &'static str {
    let x = MyType::A;
    match x {
        MyType::A => "Got A",
        MyType::B => "Got B",
    }
}

(Playground)

Another (sometimes important) difference is drop order:

#![allow(dead_code)]

enum MyType {
    A,
    B,
}

impl Drop for MyType {
    fn drop(&mut self) {
        println!("Dropped");
    }
}

impl MyType {
    fn is_a(&self) -> bool {
        match &self {
            MyType::A => true,
            MyType::B => false,
        }
    }
}

fn get_a() -> MyType {
    MyType::A
}

fn foo() {
    if get_a().is_a() {
        println!("foo")
    }
}

fn bar() {
    match get_a().is_a() {
        true => println!("bar"),
        false => (),
    }
}

fn main() {
    println!("Calling foo():");
    foo();
    println!("");
    println!("Calling bar():");
    bar();
}

(Playground)

Output:

Calling foo():
Dropped
foo

Calling bar():
bar
Dropped

You can see that in case of foo(), which uses if, the value returned by get_a() is dropped before println!("foo") gets executed. If you write the exact same with match, then the value will be dropped after println!("bar") has been executed.

This is also explicitly noted in the Rust reference in the section on temporary scopes:

The scrutinee of a match expression is not a temporary scope, so temporaries in the scrutinee can be dropped after the match expression. For example, the temporary for 1 in match 1 { ref mut z => z }; lives until the end of the statement.


This can matter in practice, for example, if you use Mutex::lock or RefCell::borrow within the boolean expression of if or in the match scrutinee.

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.