콘텐츠로 이동

Rust if let과 while let

지금까지 Option과 Result를 처리할 때 match를 써 왔어요. 그런데 실제로는 패턴 하나만 중요하고 나머지는 무시하고 싶을 때가 대부분이에요. 아무것도 안 하는 _ => {} 갈래까지 넣어서 match를 통째로 써도 동작은 하지만, 상황에 비해 격식이 너무 많아요. 이럴 때 쓰는 지름길이 if let이에요.

match를 통째로 쓰면 생기는 문제

섹션 제목: “match를 통째로 쓰면 생기는 문제”
fn main() {
let age: Option<i32> = Some(30);
match age {
Some(value) => println!("나이: {value}"),
_ => {}, // 이 경우는 사실 관심 없어요
}
}

저 _ => {}는 match의 “모든 경우를 처리해야 한다”는 규칙을 만족시키는 것 말고는 아무 일도 하지 않아요. Some일 때만 무언가를 하고 싶은 거라면, 하려는 일에 비해 코드가 너무 길어요.

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

실행 결과:

나이: 30
▶ 직접 실행해 보기

이렇게 읽으면 돼요. “age가 Some(value) 패턴과 맞으면, 안에 든 값을 value에 담고 이 블록을 실행해.” age가 None이면 블록을 그냥 건너뛰어요. 에러도 없고, 아무것도 출력되지 않아요.

일반 if처럼 if let에도 else를 붙일 수 있어요.

fn main() {
let age: Option<i32> = None;
if let Some(value) = age {
println!("나이: {value}");
} else {
println!("나이 정보가 없어요");
}
}
▶ 직접 실행해 보기

Result 페이지에서 만난 Result에도 똑같이 쓸 수 있어요.

fn main() {
let input = "42";
if let Ok(number) = input.parse::<i32>() {
println!("파싱 결과: {number}");
} else {
println!("파싱할 수 없어요");
}
}
▶ 직접 실행해 보기

if에 else if가 있듯이, if let 뒤에는 else if let이 올 수 있어요. Rust는 패턴을 차례로 시도하고, 처음으로 맞는 블록을 실행해요. 값을 찾아볼 곳이 여러 군데 있고 우선순위가 있을 때 편리해요.

fn main() {
let nickname: Option<&str> = None;
let full_name: Option<&str> = Some("Ferris Crab");
if let Some(nick) = nickname {
println!("안녕, {nick}!");
} else if let Some(name) = full_name {
println!("안녕하세요, {name}님.");
} else {
println!("안녕하세요, 손님.");
}
}

실행 결과:

안녕하세요, Ferris Crab님.
▶ 직접 실행해 보기

nickname은 None이라서 첫 번째 블록은 건너뛰어요. full_name은 Some(name)과 맞으니 그 블록이 실행되고, 마지막 else까지는 가지 않아요. 체인 중간 어디에든 else if is_admin { ... } 같은 일반 조건을 섞어 쓸 수도 있어요.

각 변수는 자기 블록 안에서만 존재해요. nick은 두 번째 블록에서 쓸 수 없고, name도 체인이 끝난 뒤에는 쓸 수 없어요. 꺼낸 값을 코드의 나머지 부분에서 계속 써야 한다면 패턴 매칭 페이지의 let-else를 살펴보세요.

while let은 같은 아이디어를 반복문에 적용한 거예요. 값이 패턴과 맞는 동안 계속 반복해요. 컬렉션을 하나씩 비워 낼 때 특히 편리해요.

fn main() {
let mut stack = vec![1, 2, 3];
while let Some(top) = stack.pop() {
println!("{top}");
}
}

실행 결과:

3
2
1
▶ 직접 실행해 보기

.pop()은 남은 값이 있는 동안에는 Some(value)를, 벡터가 비면 None을 돌려줘요. None이 나오는 순간 while let이 알아서 반복을 멈춰요. 길이를 직접 확인할 필요가 없어요.

여러 경우를 각각 다르게 처리해야 한다면 match를 쓰세요. 정말로 관심 있는 패턴이 하나뿐이고 나머지는 그냥 무시하면 된다면 if let을 쓰세요. if let은 match를 대체하는 게 아니에요. “갈래 하나만 중요한” 흔한 경우를 짧게 쓰는 방법일 뿐이에요.

let mut numbers = vec![4, 8, 15];와 0에서 시작하는 누적 합계 total로 시작하세요. while let과 .pop()으로 끝에서부터 숫자를 하나씩 꺼내서 total에 더하고, 꺼낸 숫자와 새 합계를 출력하세요. 힌트: total에는 mut가 필요하고, total += n으로 더할 수 있어요.

정답 보기
fn main() {
let mut numbers = vec![4, 8, 15];
let mut total = 0;
while let Some(n) = numbers.pop() {
total += n;
println!("꺼낸 값: {n}, 현재 합계: {total}");
}
}

실행 결과:

꺼낸 값: 15, 현재 합계: 15
꺼낸 값: 8, 현재 합계: 23
꺼낸 값: 4, 현재 합계: 27
▶ 직접 실행해 보기

.pop()은 끝에서부터 꺼내기 때문에 15가 가장 먼저 나와요.

let input = "17";을 저장하세요. 이 값이 u32로 파싱되면 그 나이가 성인(18세 이상)인지 미성년자인지 출력하세요. 파싱되지 않으면 숫자가 아니라고 출력하세요. input을 "abc"로도 바꿔서 시험해 보세요. 힌트: if let 블록 안에 일반 if / else를 넣으세요.

정답 보기
fn main() {
let input = "17";
if let Ok(age) = input.parse::<u32>() {
if age >= 18 {
println!("{age}세는 성인이에요");
} else {
println!("{age}세는 미성년자예요");
}
} else {
println!("'{input}': 숫자가 아니에요");
}
}

실행 결과:

17세는 미성년자예요
▶ 직접 실행해 보기

input = "abc"라면 파싱에 실패해서 바깥쪽 else가 실행되고, 'abc': 숫자가 아니에요가 출력돼요.

  • if let 패턴 = 값 { ... }은 값이 패턴과 맞을 때만 블록을 실행해요. 나머지 경우는 조용히 건너뛰어요.
  • 일반 if처럼 else를 붙여 맞지 않는 경우를 처리할 수 있고, else if let으로 여러 패턴을 이어 쓸 수 있어요.
  • while let 패턴 = 값 { ... }은 값이 계속 패턴과 맞는 동안 반복해요. .pop()으로 컬렉션을 비워 낼 때 유용해요.
  • 패턴 하나만 중요하다면 if let/while let을, 여러 경우를 각각 처리해야 한다면 match를 쓰세요.

이해도 점검

1. if let Some(value) = age를 실행할 때 age가 None이면 어떻게 되나요?

2. if let 대신 match를 써야 할 때는 언제인가요?

3. while let Some(top) = stack.pop() { ... }에서 반복은 언제 멈추나요?

점수: 0 / 3