Rust 초급 7편: 열거형과 패턴 매칭으로 학습 상태 표현하기
완료 여부를 bool로 저장하면 두 가지 상태만 표현할 수 있습니다. 이번에는 대기, 학습 중, 완료라는 세 상태를 만들고 각 상태에 필요한 정보도 붙여 보겠습니다. 열거형은 가능한 경우를 타입에 적는 도구입니다.

완료 여부를 bool로 저장하면 두 가지 상태만 표현할 수 있습니다. 이번에는 대기, 학습 중, 완료라는 세 상태를 만들고 각 상태에 필요한 정보도 붙여 보겠습니다. 열거형은 가능한 경우를 타입에 적는 도구입니다.
준비와 목표
6편의 구조체와 함수 사용법을 복습한 뒤 새 프로젝트를 만듭니다. 목표는 enum의 변형과 match 분기를 연결하고, 일부 경우만 처리하는 if let을 사용하는 것입니다.
cargo new study_states
cd study_states핵심 실습: 상태에 따라 다른 문장 만들기
src/main.rs 전체입니다.
enum StudyState {
Planned,
Learning { minutes: u32 },
Finished(String),
}
fn describe(state: &StudyState) -> String {
match state {
StudyState::Planned => String::from("학습 대기"),
StudyState::Learning { minutes } => format!("{minutes}분째 학습 중"),
StudyState::Finished(note) => format!("학습 완료: {note}"),
}
}
fn main() {
let planned = StudyState::Planned;
let learning = StudyState::Learning { minutes: 25 };
let finished = StudyState::Finished(String::from("match 연습"));
println!("{}", describe(&planned));
println!("{}", describe(&learning));
println!("{}", describe(&finished));
if let StudyState::Finished(note) = &finished {
println!("복습 메모: {note}");
}
}cargo run학습 대기
25분째 학습 중
학습 완료: match 연습
복습 메모: match 연습Planned에는 추가 데이터가 없습니다. Learning에는 이름이 있는 minutes 필드가 있고, Finished에는 String 하나가 들어 있습니다. 서로 다른 변형이 각기 다른 데이터를 가질 수 있습니다. 한 값은 이 중 하나의 변형입니다. 열거형 공식 설명
문자열로 상태를 기록하면 "finished"와 "finised" 같은 오타도 그냥 문자열입니다. enum을 사용하면 정의하지 않은 변형 이름은 컴파일 단계에서 드러납니다. 숫자 0, 1, 2가 무엇을 뜻하는지 따로 기억할 부담도 줄어듭니다.
match는 모든 경우를 확인합니다
describe는 &StudyState를 받아 상태를 읽고 새 String을 반환합니다. match의 각 팔은 패턴 => 실행할 식으로 작성합니다. Learning { minutes }는 변형을 확인하면서 내부 값도 이름으로 꺼내 사용합니다.
format!은 println!처럼 형식을 적용하지만 터미널에 출력하지 않고 String을 만듭니다. 이후 main이 그 결과를 출력합니다. 화면 문구를 만드는 일과 출력하는 일을 함수 경계로 나눴습니다.
새 변형 Paused를 enum에 추가하고 cargo check를 실행해 보세요. describe의 match가 그 경우를 처리하지 않는다고 알려줍니다. 빠뜨린 상태 처리를 발견하는 데 유용합니다. match 공식 설명
모든 나머지를 처리하는 패턴도 쓸 수 있지만, 상태별 동작이 중요한 이번 함수에서는 각각 명시하는 편이 낫습니다. 를 일찍 넣으면 나중에 변형을 추가해도 별도 처리를 빼먹은 것을 놓칠 수 있습니다.
한 가지 경우만 필요하면 if let
마지막 코드는 완료 메모만 보고 싶을 때입니다. if let으로 Finished에 해당하는 경우만 실행하고 나머지는 지나갑니다. 앞의 match처럼 모든 경우에 대한 값을 만들 때와 목적이 다릅니다.
&finished를 검사하므로 내부 String을 소유권 이동 없이 읽습니다. 여기서 참조를 빼고 String을 꺼내면 이후에 finished를 다시 쓰기 어려워질 수 있습니다. 패턴 매칭에도 소유권 규칙이 적용됩니다.
오류를 읽는 연습
Learning { minutes: 25 } 대신 Learning(25)를 쓰면 변형의 모양과 맞지 않습니다. 중괄호를 쓰는 필드형 변형과 소괄호를 쓰는 튜플형 변형을 구분하세요. 정의와 생성 코드의 형태가 일치해야 합니다.
match의 한 팔만 정수 1을 반환하도록 바꾸면 나머지 String과 타입이 맞지 않습니다. match는 값 하나를 만드는 표현식이므로 각 분기의 반환 타입을 맞춰야 합니다.
연습과 해설
Paused라는 데이터 없는 변형을 추가하고 describe가 "잠시 쉬는 중"을 반환하도록 수정해 보세요. main에서 paused 값을 만든 뒤 출력도 추가합니다.
해설: enum에 Paused,를 넣고 match에 StudyState::Paused => String::from("잠시 쉬는 중"),를 추가합니다. main의 let paused = StudyState::Paused;를 describe(&paused)에 전달하면 됩니다. 새 상태를 추가할 때 정의와 처리 지점을 함께 바꿨습니다.
추가 실습으로 Learning의 minutes가 0일 때 어떤 문장이 나오는지도 확인하세요. 타입이 올바른 값이라고 해서 제품에서 원하는 의미까지 자동으로 검증되는 것은 아닙니다. 필요한 규칙은 직접 구현해야 합니다.
다음 편
Rust 표준 라이브러리에도 중요한 열거형이 있습니다. 값의 부재를 표현하는 Option, 성공과 실패를 표현하는 Result입니다. 8편에서는 사용자 입력에 해당하는 문자열을 숫자로 바꾸며 이 둘을 실습합니다.
실행 확인
이 글의 핵심 실습은 Rust 1.99.1, 2024 Edition의 Linux 환경에서 확인했습니다. 의도적으로 실패하는 예제는 설명한 오류가 발생하는지 구분해 검사했습니다. 운영체제별 오류 문구는 다를 수 있습니다.



Discussion
No comments yet