Skip to content

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.

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.

fn main() {
let age: Option<i32> = Some(30);
if let Some(value) = age {
println!("Age is {value}");
}
}

Output:

Age is 30
▶ Try it Yourself

Read 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.

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");
}
}
▶ Try it Yourself

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");
}
}
▶ Try it Yourself

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.
▶ Try it Yourself

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

3
2
1
▶ Try it Yourself

.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.

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.

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 15
took 8, total is now 23
took 4, total is now 27
▶ Try it Yourself

.pop() takes from the end, which is why 15 comes out first.

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 minor
▶ Try it Yourself

With 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 if value matches Pattern — everything else is silently skipped.
  • Add else to handle the non-matching case, same as a regular if, and chain several patterns with else 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 let when you only care about one pattern; use match when you need to handle several distinctly.

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