`if let`을 사용한 간결한 제어 흐름
실패할 수 있는 패턴 하나를 간결하게 처리하는 if let의 동작, 바인딩 범위, else의 합류 의미와 match로 전환할 기준을 익힙니다.
if let은 값 하나를 패턴에 대조하고, 그 패턴이 맞을 때만 코드를 실행하는 표현식입니다.
Some만을 위한 문법은 아닙니다. 열거형 배리언트뿐 아니라 리터럴, 범위, 튜플, 구조체, 중첩 패턴과 |로 묶은 or-패턴 등 일반적인 패턴을 사용할 수 있습니다. 보통은 값에 따라 성공하거나 실패할 수 있는(refutable) 패턴을 넣습니다.
한 패턴이 맞을 때만 실행하기
다음 match는 config_max가 Some일 때만 내부 값을 출력합니다.
Some일 때만 코드를 실행하는 match
let config_max = Some(3u8);
match config_max {
Some(max) => println!("The maximum is configured to be {max}"),
_ => (),
}관심 있는 패턴이 하나뿐이라면 같은 동작을 if let으로 줄일 수 있습니다.
let config_max = Some(3u8);
if let Some(max) = config_max {
println!("The maximum is configured to be {max}");
}= 왼쪽의 Some(max)가 패턴이고 오른쪽의 config_max가 대조할 값입니다. 패턴이 맞으면 내부 값이 max에 바인딩되고 본문이 실행됩니다. 맞지 않으면 본문을 건너뜁니다.
위와 같은 기본형에서 max의 범위는 패턴이 성립한 본문 블록입니다. else 블록이나 if let 표현식 뒤에서는 max를 사용할 수 없습니다. Rust 2024 에디션의 let 체인에서는 앞 패턴의 바인딩을 뒤 조건과 최종 본문에서도 사용할 수 있습니다. 2021을 포함한 2024 이전 에디션에서는 중첩 if let으로 같은 흐름을 표현합니다. 공식 if 표현식의 에디션 차이를 기준으로 구분합니다. 이 절의 예제는 체인이 없는 기본형입니다. 어느 에디션에서도 불일치 경로에는 그 패턴의 바인딩이 생기지 않습니다.
if let의 두 실행 경로와 match 전환 기준
if let의 값을 평가해 일치하면 max 등 바인딩을 만들어 본문만 실행하고 불일치면 else/건너뜀으로 가는 실제 제어다. else는 특정 두 번째 패턴이 아니며 기본형 바인딩은 성공 본문 범위라는 경계를 단계에 남긴다.
IF LET · ONE PATTERN
if let은 한 패턴의 성공과 모든 불일치를 나눈다
관심 있는 패턴이 맞으면 바인딩을 만들어 본문을 실행합니다. else는 패턴에 맞지 않은 모든 값을 한 경로로 모읍니다.
if let P = value
패턴 일치
본문을 실행하고 P가 만든 바인딩을 그 본문 안에서 사용합니다.
패턴 불일치
바인딩은 생기지 않습니다. 모든 불일치 값은 하나의 else로 가거나, else가 없으면 건너뜁니다.
| 판단 축 | if let |
match |
|---|---|---|
| 관심 범위 | 한 패턴 또는 한 or-패턴이 중요함 | 여러 경우가 서로 다른 의미를 가짐 |
| 나머지 값 | 무시하거나 하나의 else로 합침 |
모든 가능성을 갈래로 덮음 |
| 전환 신호 | 짧은 부수 효과 또는 단순한 두 경로 | 갈래별 결과·매치 가드·의미 있는 경우가 늘어남 |
| 변경 감지 | 불일치 값의 종류를 검사하지 않음 | 배리언트를 명시하면 추가 시 누락이 오류가 됨 |
if let유지- 한 패턴 또는 or-패턴만 중요하고, 나머지는 무시하거나 하나의
else로 처리합니다. match로 전환- 경우별 결과, 매치 가드, 서로 다른 의미가 늘어나 모든 갈래를 한눈에 검토해야 합니다.
match의 _도 철저성을 만족하지만 미래 배리언트까지 기존 기본 갈래에 넣습니다. 새 배리언트마다 의미를 재검토해야 한다면 닫힌 열거형의 현재 배리언트를 명시적으로 나열합니다.
if let은 안전하지 않은 축약이 아니라 불일치 값을 의도적으로 한 경로로 접는 표현식입니다. 그 접힘이 더 이상 도메인 의미와 맞지 않을 때 match로 펼칩니다.이 예의 조건
- 본문과 원 도표에 제시된 사례의 입력·조건을 사용합니다. 설명용 수치나 독립 비교 경로는 각 단계에 표시합니다.
- 현재 상태·값과 전환 근거의 위치는 모든 단계에서 같습니다. 조건이 맞지 않거나 실행되지 않은 경로를 성공한 동작으로 해석하지 않습니다.
관계도의 보충 설명
| 범위 | 원문 |
|---|---|
| 관계·조건 설명 | 원래의 4노드와 3간선과 같다. if let 값 평가→패턴 조건. 예는 바인딩이 있는 본문으로, 아니요는 바인딩 없는 else 또는 건너뜀으로 우회한다. 성공 본문에서 else로 이어지는 간선은 없다. 기본형의 바인딩 범위를 보존하며 2024 let 체인 확장과 구별한다. 세로 도표에서도 성공/모든 불일치의 관계가 같고 아래 if let/match 설계 비교표를 그대로 남긴다. |
| 관찰·상황 | 값·보충 정보 | 변화·이유 |
|---|---|---|
| 패턴 입력 본문의 입력 Some(201)을 검사한다. |
|
|
| 성공 본문 code=201 바인딩으로 성공 본문을 실행한다. |
|
|
| 후속 경계 불일치 사본에는 code 바인딩이 없다. |
|
|
if let의 두 실행 경로와 match 전환 기준
if let의 값을 평가해 일치하면 max 등 바인딩을 만들어 본문만 실행하고 불일치면 else/건너뜀으로 가는 실제 제어다. else는 특정 두 번째 패턴이 아니며 기본형 바인딩은 성공 본문 범위라는 경계를 단계에 남긴다.
IF LET · ONE PATTERN
if let은 한 패턴의 성공과 모든 불일치를 나눈다
관심 있는 패턴이 맞으면 바인딩을 만들어 본문을 실행합니다. else는 패턴에 맞지 않은 모든 값을 한 경로로 모읍니다.
if let P = value
패턴 일치
본문을 실행하고 P가 만든 바인딩을 그 본문 안에서 사용합니다.
패턴 불일치
바인딩은 생기지 않습니다. 모든 불일치 값은 하나의 else로 가거나, else가 없으면 건너뜁니다.
| 판단 축 | if let |
match |
|---|---|---|
| 관심 범위 | 한 패턴 또는 한 or-패턴이 중요함 | 여러 경우가 서로 다른 의미를 가짐 |
| 나머지 값 | 무시하거나 하나의 else로 합침 |
모든 가능성을 갈래로 덮음 |
| 전환 신호 | 짧은 부수 효과 또는 단순한 두 경로 | 갈래별 결과·매치 가드·의미 있는 경우가 늘어남 |
| 변경 감지 | 불일치 값의 종류를 검사하지 않음 | 배리언트를 명시하면 추가 시 누락이 오류가 됨 |
if let유지- 한 패턴 또는 or-패턴만 중요하고, 나머지는 무시하거나 하나의
else로 처리합니다. match로 전환- 경우별 결과, 매치 가드, 서로 다른 의미가 늘어나 모든 갈래를 한눈에 검토해야 합니다.
match의 _도 철저성을 만족하지만 미래 배리언트까지 기존 기본 갈래에 넣습니다. 새 배리언트마다 의미를 재검토해야 한다면 닫힌 열거형의 현재 배리언트를 명시적으로 나열합니다.
if let은 안전하지 않은 축약이 아니라 불일치 값을 의도적으로 한 경로로 접는 표현식입니다. 그 접힘이 더 이상 도메인 의미와 맞지 않을 때 match로 펼칩니다.이 예의 조건
- 본문과 원 도표에 제시된 사례의 입력·조건을 사용합니다. 설명용 수치나 독립 비교 경로는 각 단계에 표시합니다.
- 현재 상태·값과 전환 근거의 위치는 모든 단계에서 같습니다. 조건이 맞지 않거나 실행되지 않은 경로를 성공한 동작으로 해석하지 않습니다.
관계도의 보충 설명
| 범위 | 원문 |
|---|---|
| 관계·조건 설명 | 원래의 4노드와 3간선과 같다. if let 값 평가→패턴 조건. 예는 바인딩이 있는 본문으로, 아니요는 바인딩 없는 else 또는 건너뜀으로 우회한다. 성공 본문에서 else로 이어지는 간선은 없다. 기본형의 바인딩 범위를 보존하며 2024 let 체인 확장과 구별한다. 세로 도표에서도 성공/모든 불일치의 관계가 같고 아래 if let/match 설계 비교표를 그대로 남긴다. |
| 관찰·상황 | 값·보충 정보 | 변화·이유 |
|---|---|---|
| 패턴 입력 본문의 입력 Some(201)을 검사한다. |
|
|
| 성공 본문 code=201 바인딩으로 성공 본문을 실행한다. |
|
|
| 후속 경계 불일치 사본에는 code 바인딩이 없다. |
|
|
or-패턴도 사용할 수 있습니다. 각 대안이 바인딩을 만든다면 이름, 타입, 바인딩 방식이 서로 같아야 합니다.
let status = Some(201u16);
if let Some(code @ (200 | 201)) = status {
println!("accepted: {code}");
}이 예제의 패턴은 Some(200) 또는 Some(201)에만 맞고, 맞은 값을 code에 바인딩합니다. 그 밖의 Some 값과 None은 모두 불일치입니다.
if let은 대략 “첫 갈래만 쓰고 나머지는 _로 처리하는 match”에 해당하는 문법 설탕입니다. 코드가 짧아지는 대신, match가 제공하는 모든 경우 검사를 포기하므로 불일치 값을 정말 무시해도 되는지 설계자가 판단해야 합니다.
else는 모든 불일치 값을 한 경로로 모읍니다
if let에는 else를 붙일 수 있습니다. else는 특정한 두 번째 패턴이 아니라, 앞의 패턴과 맞지 않은 모든 값을 한꺼번에 처리합니다.
쿼터 동전이면 주 이름을 출력하고, 쿼터가 아닌 모든 동전은 개수만 센다고 해봅시다. match로는 다음과 같이 작성할 수 있습니다.
let mut count = 0;
match coin {
Coin::Quarter(state) => println!("State quarter from {state:?}!"),
_ => count += 1,
}두 경로만 구분하면 되므로 if let ... else로 같은 뜻을 표현할 수 있습니다.
let mut count = 0;
if let Coin::Quarter(state) = coin {
println!("State quarter from {state:?}!");
} else {
count += 1;
}이 두 발췌의 coin은 앞 절에서 정의한 주 데이터를 가진 Coin 값이 필요합니다. count는 각 발췌에서 0으로 시작하므로, 여러 동전을 누적해서 세는 완전한 프로그램과는 구분합니다.
예시 아래는 Coin·UsState 정의와 호출을 함께 둔 별도 src/main.rs입니다. 동전 값은 함수로 넘기고, 누적 count는 &mut로 빌려 갱신합니다. 같은 이름의 원 Coin 정의를 중복해서 붙이지 않습니다.
#[derive(Debug)]
enum UsState { Alabama, Alaska }
enum Coin {
Penny,
Nickel,
Dime,
Quarter(UsState),
}
fn observe_coin(coin: Coin, count: &mut u8) {
if let Coin::Quarter(state) = coin {
println!("State quarter from {state:?}!");
} else {
*count += 1;
}
}
fn main() {
let mut count = 0;
observe_coin(Coin::Quarter(UsState::Alaska), &mut count);
observe_coin(Coin::Nickel, &mut count);
observe_coin(Coin::Dime, &mut count);
println!("non-quarter: {count}");
}Alaska 쿼터는 주 메시지만 출력하고 count는 0을 유지합니다. Nickel은 1, Dime은 2로 올립니다. 따라서 표시되는 내용은 State quarter from Alaska!와 non-quarter: 2입니다. else가 Nickel을 Dime으로 분해하는 것이 아니라, 두 값 모두 앞의 Quarter 패턴에 맞지 않아 같은 동작을 실행합니다. 동전 세 개를 다뤘지만 count는 비쿼터의 수만 셉니다.
스스로 불일치 경로 확인하기
마지막 println 앞에 Alabama 쿼터와 Penny를 순서대로 한 번씩 전달하면 주 메시지와 최종 count는 어떻게 바뀔까요? 처음 status 예제의 입력을 Some(202u16), None::<u16>으로 각각 바꾸면 accepted가 출력될까요? 같은 이름의 code를 if let 뒤에서 읽을 수 있는지도 판단합니다.
합류와 바인딩 범위 해설
Alabama 메시지가 추가되지만 그때 count는 2로 유지되고 Penny 뒤에는 3입니다. 전체 동전 수 5와 비쿼터 수 3을 구분합니다. Some(202)와 None은 모두 Some(code @ (200 | 201))에 맞지 않아 accepted를 출력하지 않습니다. 이 기본형의 code는 성공한 본문 안에서만 존재하므로 표현식 뒤에서는 읽을 수 없습니다. 불일치 값들을 구별하지 않는 else와 바인딩의 사용 범위는 별개입니다.
state는 Coin::Quarter(state)가 맞은 본문에서만 존재합니다. else에는 Penny, Nickel, Dime처럼 패턴과 맞지 않은 값이 모두 들어오지만, 어떤 배리언트였는지나 그 내부 데이터는 새 패턴으로 분해되지 않습니다.
if let ... else도 표현식이므로 두 블록에서 같은 타입의 값을 만들 수 있습니다. 다만 여러 배리언트가 서로 다른 값을 만들거나, 갈래마다 별도의 조건을 붙이기 시작하면 match가 분기와 결과를 더 명확하게 드러냅니다.
match로 전환할 기준
다음과 같은 경우에는 간결함보다 match의 구조가 더 중요합니다.
- 여러 경우가 각각 다른 의미와 동작을 가질 때
- 갈래별 반환값으로 하나의 값을 만들 때
- 패턴마다 매치 가드를 붙여 조건을 더 좁힐 때
- 열거형의 모든 배리언트를 의식적으로 검토하고 누락을 컴파일 오류로 만들고 싶을 때
match는 모든 가능성을 덮어야 하지만, _도 모든 나머지 값을 덮는 유효한 포괄 패턴입니다. 따라서 닫힌 열거형에서 기존 배리언트를 _로 묶어 두면 나중에 새 배리언트가 추가되어도 그 값이 기존 포괄 갈래로 들어가며, “새 의미를 별도로 처리해야 하는가?”를 컴파일러가 다시 묻지 못할 수 있습니다.
정말 같은 기본 동작이 맞을 때만 _를 사용하세요. 새 배리언트마다 동작을 재검토해야 한다면 현재 배리언트를 명시적으로 나열하는 편이 낫습니다. 반대로 다른 크레이트가 공개한 #[non_exhaustive] 열거형은 미래 배리언트를 허용하므로 외부에서 매칭할 때 포괄 갈래가 필요합니다. 이때는 포괄 경로가 무엇을 보장하는지 코드와 테스트로 분명히 남겨야 합니다.
Option<T>에서 Some만 부수 효과로 처리하고 None은 의도적으로 무시한다면 if let이 잘 맞습니다. Some과 None이 각각 결과를 만들거나, 경우별 조건과 처리가 늘어난다면 match를 선택하세요.
지금까지 구조체와 열거형으로 도메인 값을 표현하고, Option<T>로 값의 부재를 타입에 담고, match와 if let으로 그 값을 안전하게 분해하는 방법을 살펴봤습니다. 다음 장에서는 잘 조직된 API를 만들기 위한 러스트의 모듈을 다룹니다.