Understanding if let statement

Hi. I'm new in Rust.

I've been learning for a while. Normally, I'm a .NET Core developer.

I'm trying to understand the if let statement. I'm using this source;

https://doc.rust-lang.org/book/ch06-03-if-let.html

So, I'm a little bit stuck at this point. How does this feature work in Rust?

For example,

if let Some(THIS_WILL_CHECKED_FROM_VARIABLEA) = VARIABLEA 

Is this true? I tried to write a bad code like that;

struct Permissions {
    user_id: i32,
    admin: bool
}

fn is_admin(permission: Option<Permissions>) -> bool {

    if let Some(Permissions) = permission {
        return true
    }

    return false
}

According to my example, this function expects an optional Permission struct. If this struct exists, we can return true. But if not exists, we can return false.

Am I right about this?

So,

LEFT = if let Some(Permissions)
RIGHT = permission

Is LEFT just a checker?

Thanks

I don’t understand this question, could you elaborate what β€œTHIS_WILL_CHECKED_FROM_VARIABLEA” and β€œVARIABLEA” are supposed to mean?

I don’t know what you mean by β€œa bad code”. In order to understand what this code will do, you should first fix this warning it produces

warning: variable `Permissions` should have a snake case name
 --> src/lib.rs:8:17
  |
8 |     if let Some(Permissions) = permission {
  |                 ^^^^^^^^^^^ help: convert the identifier to snake case: `permissions`
  |
  = note: `#[warn(non_snake_case)]` on by default

So changing the variable name, the code becomes something like e.g.

struct Permissions {
    user_id: i32,
    admin: bool
}

fn is_admin(permission: Option<Permissions>) -> bool {

    if let Some(p) = permission {
        return true
    }

    return false
}

Also, it still produces this warning

warning: unused variable: `p`
 --> src/lib.rs:8:17
  |
8 |     if let Some(p) = permission {
  |                 ^ help: if this is intentional, prefix it with an underscore: `_p`
  |
  = note: `#[warn(unused_variables)]` on by default

which helps understanding what’s going on:

The check

if let Some(p) = permission

will inspect the permission variable (the right hand side of the ==, which is of type Option<Permissions>, and match it against the pattern β€œSome(p)”. The p in this pattern is the name of a new variable that is introduced (locally) by the if let statement.

An Option<Permissions> value can either be None, or Some(Permissions { user_id: …, admin: … }).

The idea behind Option is that it captures a similar idea like null in many other existing languages. The types can then be used to distinguish nullable vs. not nullable value, if you will; i.e. Option<Permissions> is nullable, and Permissions isn’t. The None value plays the role of null, and Some(…value…) is the non-null case, the Some(…) constructor wraps the contained value. This is a useful principle because it means that in order to access a value that is nullable, you’ll have to unwrap this extra β€œlayer” around it first, which means you need to explicitly to a null-check.

Anyways… back to if let: The pattern Some(p) matches any Some(…) value, and it only doesn’t match None.

So running

if let Some(p) = permission {
   …CODE…
}

effectively distinguishes whether or not permission is None; only if it isn’t None, i.e. it is Some(…value…), then the pattern Some(p) matches. This pattern matching will then introduce and bind the pattern variable p to the corresponding value wrapped in the Some. The variable p is available for the …CODE… inside of the block attached to the if let statement.

In this case, this block just does return true, so it doesn’t use the value p at all. Hence also the β€œunused variable” warning I pointed out earlier. The code you’re implementing can be simplified by using the .is_some() method of Option, as follows

fn is_admin(permission: Option<Permissions>) -> bool {
    permission.is_some()
}

I’m not quite following the β€œif this struct exists” terminology, but if what you’re asking about matches my explanation above, then the answer might be yes.


Also note that (as the book somewhat explains, too, IIRC) if let is essentially just syntactic sugar for a match.

A statement

if let π˜—π˜ˆπ˜›π˜›π˜Œπ˜™π˜• = π˜Œπ˜Ÿπ˜—π˜™π˜Œπ˜šπ˜šπ˜π˜–π˜• {
    π˜π˜π˜™π˜šπ˜›_π˜‰π˜“π˜–π˜Šπ˜’
}

is just a shorthand for

if let π˜—π˜ˆπ˜›π˜›π˜Œπ˜™π˜• = π˜Œπ˜Ÿπ˜—π˜™π˜Œπ˜šπ˜šπ˜π˜–π˜• {
    π˜π˜π˜™π˜šπ˜›_π˜‰π˜“π˜–π˜Šπ˜’
} else {}

And a statement

if let π˜—π˜ˆπ˜›π˜›π˜Œπ˜™π˜• = π˜Œπ˜Ÿπ˜—π˜™π˜Œπ˜šπ˜šπ˜π˜–π˜• {
    π˜π˜π˜™π˜šπ˜›_π˜‰π˜“π˜–π˜Šπ˜’
} else {
    π˜šπ˜Œπ˜Šπ˜–π˜•π˜‹_π˜‰π˜“π˜–π˜Šπ˜’
}

is just a shorthand for

match π˜Œπ˜Ÿπ˜—π˜™π˜Œπ˜šπ˜šπ˜π˜–π˜• {
    π˜—π˜ˆπ˜›π˜›π˜Œπ˜™π˜• => {
        π˜π˜π˜™π˜šπ˜›_π˜‰π˜“π˜–π˜Šπ˜’
    }
    _ => {
        π˜šπ˜Œπ˜Šπ˜–π˜•π˜‹_π˜‰π˜“π˜–π˜Šπ˜’
    }
}

So if you’re having a better understanding of match than if let, your code is equivalent to

fn is_admin(permission: Option<Permissions>) -> bool {

    match permission {
        Some(p) => { return true }
        _ => {}
    }

    return false
}
5 Likes

Thank you so much, @steffahn, Actually, my all questions are the same in different ways :slight_smile:

Now, I understand, the LEFT side contains a new variable. This variable only exists, if the right value is not a None value.

I meant by bad code it's really a bad code and may not explain what I'm trying to understand.

I’m not quite following the β€œif this struct exists” terminology, but if what you’re asking about matches my explanation above, then the answer might be yes.

This showed me that I misunderstood before you explain me :slight_smile:

Some languages like C# didn't have a feature like that. C# also has this feature with different syntax.

Thank you so much. I'm so happy to understand this. Thanks for your help. I'll mark this as a solution.

1 Like

I’m not really familiar with C# at all. Googling for β€œpattern matching” gives an example like

int? maybe = 12;

if (maybe is int number)
{
    Console.WriteLine($"The nullable int 'maybe' has the value {number}");
}
else
{
    Console.WriteLine("The nullable int 'maybe' doesn't hold a value");
}

and the (moral) equivalent of this in Rust syntax would indeed involve if let and look like

let maybe: Option<i32> = Some(12);

if let Some(number) = maybe {
    println!("The optional i32 'maybe' has the value {number}");
} else {
    println!("The optional i32 'maybe' doesn't hold a value");
}
1 Like

Yes, it works like that :slight_smile:

Actually, they looked me the same and much clear for me now :slight_smile:

I wonder what will happen when I start to lifetime section :slight_smile:

Probably, I'll give a headache to users

that's what's beautiful about Rust, enjoying the invigorating feeling of victory when one finally understands and starts using some new concept "like a king"... :smiley:

(i'm getting many of those in the past weeks/months while trying to get better with this neat language with help both here and on Rust discord server)

2 Likes