spechter
spechter

Reputation: 2368

Rust Lang: What is "if let Some(x) = x" doing?

I'm working on a rust project written a couple of years ago, and have come across this piece of code, which is literally:

let mut values = vec![];
for x in maybe_values {
    if let Some(x) = x {
        values.push(Arc::new(x));
    }
}

I understand that "if let" introduces a pattern-matching if (Which seems to be a poor re-use of the keyword "let", but I will get over that - If anyone can help me with a mental mnemonic to make sense of "let" here, please do!).

But what is the test Some(x) = x doing?

From my testing, it seems to be a trick/idiom to both a) test that the loop variant 'x' is Some(), and also b) end up with the unwrap()ped value in x.

But I can't fully explain it to myself, and can't find reference to this being an idiom anywhere.

Hope you can help my Rust education path. Thanks.

Upvotes: 32

Views: 28779

Answers (4)

omgirok
omgirok

Reputation: 175

Basically, maybe_values is full of Options. Some are None and some values are Some(x)

if the maybe_value is not a None (meaning it is able to match Some(x)), it will be appended to the values vec.

This for loop essentially cleans out the None/NULL values of the maybe_values vec into a vec (for_sure_are_values) where all the values DO exist.

Upvotes: 0

Lucio M. Tato
Lucio M. Tato

Reputation: 5815

My 2 cents: Another good mnemonic to understand/remember if-let is as a shortcut to a very common pattern: if a.is_some() { let b = a.unwrap() ... } :

Full pattern:

let mut values = vec![];
for x_1 in maybe_values {
    if x_1.is_some() {
       let x_2 = x_1.unwrap()
       values.push(Arc::new(x_2));
    }
}

the shortcut if-let means joining the if x_1.is_some() and the let x_2 = x.unwrap() in a single if-let statement

With the shortcut:

let mut values = vec![];
for x_1 in maybe_values {
    if let Some(x_2) = x_1 {
       values.push(Arc::new(x_2));
    }
}

Upvotes: 3

Locke
Locke

Reputation: 8980

This is a shorthand for using a full match statement when you only care about matching a single use case.

So this block of code:

if let x = y {
   foo();
} else {
   bar();
}

Is equivalent to using a full match:

match y {
    x => {
        foo();
    }
    _ => {
        bar();
    }
}

For your specific case, it is equivalent to this. The inner x uses the same name as the outer variable which can be confusing, but they are two separate values.

let mut values = vec![];
for x in maybe_values {
    match x {
        Some(y) => values.push(Arc::new(y)),
        _ => {},
    }
}

Upvotes: 35

Silvio Mayolo
Silvio Mayolo

Reputation: 70347

There are two completely different variables in play here. It's equivalent to.

let mut values = vec![];
for x_1 in maybe_values {
  if let Some(x_2) = x_1 {
    values.push(Arc::new(x_2));
  }
}

In Rust, the right-hand side of a let is evaluated with the left-hand variable not in scope, so when the if let is evaluated, the outer x is still in-scope. Then, if it's a Some value, we make a new variable x which contains the inside of the Option. This variable shadows the previous x, making it inaccessible inside the if statement (in the same way that a function argument called x would render a global variable named x inaccessible by shadowing).

Upvotes: 15

Related Questions