if let
For some use cases, when matching enums, match
is awkward. For example:
# #![allow(unused_variables)] #fn main() { // Make `optional` of type `Option<i32>` let optional = Some(7); match optional { Some(i) => { println!("This is a really long string and `{:?}`", i); // ^ Needed 2 indentations just so we could destructure // `i` from the option. }, _ => {}, // ^ Required because `match` is exhaustive. Doesn't it seem // like wasted space? }; #}
if let
is cleaner for this use case and in addition allows various
failure options to be specified:
fn main() { // All have type `Option<i32>` let number = Some(7); let letter: Option<i32> = None; let emoticon: Option<i32> = None; // The `if let` construct reads: "if `let` destructures `number` into // `Some(i)`, evaluate the block (`{}`). if let Some(i) = number { println!("Matched {:?}!", i); } // If you need to specify a failure, use an else: if let Some(i) = letter { println!("Matched {:?}!", i); } else { // Destructure failed. Change to the failure case. println!("Didn't match a number. Let's go with a letter!"); }; // Provide an altered failing condition. let i_like_letters = false; if let Some(i) = emoticon { println!("Matched {:?}!", i); // Destructure failed. Evaluate an `else if` condition to see if the // alternate failure branch should be taken: } else if i_like_letters { println!("Didn't match a number. Let's go with a letter!"); } else { // The condition evaluated false. This branch is the default: println!("I don't like letters. Let's go with an emoticon :)!"); }; }
In the same way, if let
can be used to match any enum value:
// Our example enum enum Foo { Bar, Baz, Qux(u32) } fn main() { // Create example variables let a = Foo::Bar; let b = Foo::Baz; let c = Foo::Qux(100); // Variable a matches Foo::Bar if let Foo::Bar = a { println!("a is foobar"); } // Variable b does not match Foo::Bar // So this will print nothing if let Foo::Bar = b { println!("b is foobar"); } // Variable c matches Foo::Qux which has a value // Similar to Some() in the previous example if let Foo::Qux(value) = c { println!("c is {}", value); } }
Another benefit: if let
allows to match enum non-parameterized variants, even if the enum doesn't #[derive(PartialEq)]
, neither we implement PartialEq
for it. In such case, classic if Foo::Bar==a
fails, because instances of such enum are not comparable for equality. However, if let
works.
Would you like a challenge? Fix the following example to use if let
:
// This enum purposely doesn't #[derive(PartialEq)],
// neither we implement PartialEq for it. That's why comparing Foo::Bar==a fails below.
enum Foo {Bar}
fn main() {
let a = Foo::Bar;
// Variable a matches Foo::Bar
if Foo::Bar == a {
// ^-- this causes a compile-time error. Use `if let` instead.
println!("a is foobar");
}
}