Reputation: 2368
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
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
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
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
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