Rust 기초: match로 시작하는 패턴 매칭
Rust를 쓰는 이유 패턴 매칭 시스템이라는 말이 있을 정도로 Rust는 강력한 패턴 매칭 시스템을 자랑하는데요.
이러한 패턴 매칭은 바로 안전성과 가독성을 동시에 만족시키는 표현식 match
에서 시작됩니다.
이 글에서는 match
표현식의 기본 사용법부터 자주 사용되는 패턴 매칭 기법, 그리고 실무에서 볼 수 있는 활용 예시까지 살펴보겠습니다.
기본 문법
match
는 어떤 값에 대해 가능한 여러 가지 경우를 “패턴”으로 나눠 처리할 수 있게 해줍니다.
다른 프로그래밍 언어에서는 보통 switch
문으로 비슷한 기능을 제공하지만, 아래 보시다시피 문법이 훨씬 간결하고 명료합니다.
fn print_number(n: i8) {
match number {
1 => println!("하나"),
2 => println!("둘"),
3 => println!("셋"),
_ => println!("다수"),
}
}
match
표현식의 문법을 좀 더 자세히 들여다볼까요?
- 각 팔(arm)은
(조건) => (결과)
형태로 작성합니다. _
는 와일드카드로서 위에 있는 팔에서 부합하지 않은 나머지 모든 경우에 해당됩니다.- 하나의 경우라도 빠뜨리면 컴파일 에러가 나므로 반드시 모든 경우에 대해서 처리를 해야 합니다.
다른 언어의 switch
문과 달리 Rust의 match
는 문장(statement) 표현식(expression)입니다.
따라서 변수에 저장할 수도 있고 함수에 인자로 넘기거나 반환할 수도 있습니다.
예를 들어, 각 팔에서 바로 결과 값을 출력하는 대신에 결과 값을 변수에 저장한 후에 출력하도록 코드를 살짝 바꿔보겠습니다.
fn print_number(n: i8) {
let result = match number {
1 => "하나",
2 => "둘",
3 => "셋",
_ => "다수",
};
println!("{result}");
}
범위 매칭
match
표현식을 사용하는 가장 간단한 방법은 범위 매칭입니다.
예를 들어, 아래 함수는 양의 정수의 숫자를 입력으로 받아서 학점 문자열을 반환합니다.
fn into_grade(score: u8) -> &'static str {
match score {
91..=100 => "A",
81..=90 => "B",
71..=80 => "C",
61..=70 => "D",
_ => "F",
}
}
튜플 매칭
match
표현식은 튜플을 매칭할 때도 사용할 수 있습니다.
fn print_location(x: i32, y: i32) {
match (x, y) {
(0, 0) => println!("시작점에 있음"),
(0, _) => println!("Y 축에 있음"),
(_, 0) => println!("X 축에 있음"),
_ => println!("다른 곳에 있음"),
}
}
열거형 매칭
match
표현식은 특히 열거형과 궁합이 좋습니다.
예를 들어, Start
, Move
, Quit
값으로 이루어진 Action
열거형을 다음고 같이 처리할 수 있습니다.
enum Action {
Start(i32, i32),
Move { x: i32, y: i32 },
Quit,
}
fn handle(msg: Action) {
match msg {
Action::Start(x, y) => println!("Start at ({x}, {y})"),
Action::Move { x, y } => println!("Move to ({x}, {y})"),
Action::Quit => println!("Bye!"),
}
}
fn main() {
handle(Action::Start(0, 0));
handle(Action::Move { x: 1, y: 2 });
handle(Action::Quit);
}
Start at (0, 0)
Move to (1, 2)
Bye!
가드와 함께 사용
패턴과 함께 조건을 붙이고 싶을 땐 if
가드(guard)를 쓸 수 있습니다.
fn check_number(n: i32) {
match n {
x if x % 2 == 0 => println!("Even"),
_ => println!("Odd"),
}
}
matches!() 매크로
아주 간단하게 패턴이 일치하는지만 검사해야할 때는 match
표현식을 쓰는 것이 장황하게 느껴질 수 있습니다.
이럴 때는 matches!()
메크로를 사용할 수도 있습니다.
fn main () {
let score = Some(95);
if matches!(score, Some(v) if v >= 70) {
println!("합격");
}
}
특히 반복자(Iterator)의 filter()
함수와 자주 사용됩니다.
fn main() {
let scores = [None, Some(70), None, Some(80), None];
assert_eq!(scores.iter().filter(|x| matches!(x, None)).count(), 3);
}
마치며
지금까지 match
표현식을 어떻게 사용하는지 다양한 예제를 통해서 대해서 살펴보았습니다.