Rust If Let and While Let
You’ve used match to handle Option and Result. Most of the time, though, you only actually care about one pattern and want to ignore everything else. Writing a full match with a _ => {} arm just to do nothing works, but it’s more ceremony than the situation needs. if let is the shortcut.
The problem with a full match here
Section titled “The problem with a full match here”fn main() { let age: Option<i32> = Some(30);
match age { Some(value) => println!("Age is {value}"), _ => {}, // we don't actually care about this case }}That _ => {} is doing nothing except satisfying match’s “every case must be handled” rule. If all you want is to act on Some, this is more code than the idea deserves.
if let
Section titled “if let”fn main() { let age: Option<i32> = Some(30);
if let Some(value) = age { println!("Age is {value}"); }}Output:
Age is 30Read it as: “if age matches the pattern Some(value), run this block, with value bound to what was inside.” If age is None, the block is simply skipped — no error, nothing printed.
Adding an else
Section titled “Adding an else”if let can have an else, just like a regular if:
fn main() { let age: Option<i32> = None;
if let Some(value) = age { println!("Age is {value}"); } else { println!("No age given"); }}if let with Result
Section titled “if let with Result”Works the same way with Result, which you met on the Result page:
fn main() { let input = "42";
if let Ok(number) = input.parse::<i32>() { println!("Parsed: {number}"); } else { println!("Couldn't parse that"); }}Chaining with else if let
Section titled “Chaining with else if let”Just like if has else if, if let can be followed by else if let. Rust tries each pattern in turn and runs the first block that matches. It’s handy when you have a few places to look for a value, in order of preference:
fn main() { let nickname: Option<&str> = None; let full_name: Option<&str> = Some("Ferris Crab");
if let Some(nick) = nickname { println!("Hi, {nick}!"); } else if let Some(name) = full_name { println!("Hello, {name}."); } else { println!("Hello, stranger."); }}Output:
Hello, Ferris Crab.nickname is None, so the first block is skipped. full_name matches Some(name), so that block runs, and the final else is never reached. You can also mix in a plain condition, such as else if is_admin { ... }, anywhere in the chain.
Each variable only exists inside its own block: nick can’t be used in the second block, and name can’t be used after the chain ends. If you need the unpacked value for the rest of your code, look at let-else on the Pattern Matching page.
while let
Section titled “while let”while let applies the same idea to a loop: keep looping as long as a value matches a pattern. It’s especially handy for draining a collection:
fn main() { let mut stack = vec![1, 2, 3];
while let Some(top) = stack.pop() { println!("{top}"); }}Output:
321.pop() returns Some(value) while there’s something left, and None once the vector is empty — at which point while let stops the loop automatically. No manual length check needed.
if let vs match — which to use
Section titled “if let vs match — which to use”Use match when you genuinely need to handle multiple cases differently. Reach for if let when there’s really only one pattern you care about and everything else should just be ignored. It’s not a replacement for match — just a shorter way to write the common “I only care about one arm” case.
Practice
Section titled “Practice”1. Sum a stack
Section titled “1. Sum a stack”Start with let mut numbers = vec![4, 8, 15]; and a running total of 0. Use while let with .pop() to take numbers off the end one at a time, add each one to total, and print the number and the new total. Hint: total needs mut, and total += n adds to it.
Show solution
fn main() { let mut numbers = vec![4, 8, 15]; let mut total = 0;
while let Some(n) = numbers.pop() { total += n; println!("took {n}, total is now {total}"); }}Output:
took 15, total is now 15took 8, total is now 23took 4, total is now 27.pop() takes from the end, which is why 15 comes out first.
2. Check an age from text
Section titled “2. Check an age from text”Store let input = "17";. If it parses as a u32, print whether that age is an adult (18 or over) or a minor. If it doesn’t parse, print that it’s not a number. Try changing input to "abc" too. Hint: put a normal if / else inside the if let block.
Show solution
fn main() { let input = "17";
if let Ok(age) = input.parse::<u32>() { if age >= 18 { println!("{age} is an adult"); } else { println!("{age} is a minor"); } } else { println!("'{input}' is not a number"); }}Output:
17 is a minorWith input = "abc", the parse fails, the outer else runs, and it prints 'abc' is not a number.
if let Pattern = value { ... }runs the block only ifvaluematchesPattern— everything else is silently skipped.- Add
elseto handle the non-matching case, same as a regularif, and chain several patterns withelse if let. while let Pattern = value { ... }loops as long as the value keeps matching, useful for draining a collection with.pop().- Use
if let/while letwhen you only care about one pattern; usematchwhen you need to handle several distinctly.
Quick check
Section titled “Quick check”Quick check
1. What happens when if let Some(value) = age runs and age is None?
2. When should you reach for match instead of if let?
3. In while let Some(top) = stack.pop() { ... }, when does the loop stop?
Score: 0 / 3
Read more
Section titled “Read more”- Concise Control Flow with if let and let…else — The Rust Book: the official introduction, which also covers let-else.
- if let — Rust by Example: runnable examples with
Optionand your own enums, and whyif letbeatsmatchfor one pattern. - while let — Rust by Example: a loop rewritten from
loop+matchinto a shorterwhile let. if letpatterns — The Rust Reference: the exact rules, including howif letrelates tomatch.