콘텐츠로 이동

Rust match

match는 값을 여러 패턴과 차례로 비교하고, 맞는 패턴의 코드를 실행해요. switch 문을 써 본 적이 있다면 그보다 훨씬 강력한 버전이라고 생각하면 돼요. 써 본 적이 없어도 괜찮아요. 일찍 배워 두면 정말 쓸모가 많은 문법이에요.

fn main() {
let day = 3;
match day {
1 => println!("월요일"),
2 => println!("화요일"),
3 => println!("수요일"),
4 => println!("목요일"),
5 => println!("금요일"),
_ => println!("주말"),
}
}

실행 결과:

수요일
▶ 직접 실행해 보기

_는 나머지 전부를 처리하는 패턴이에요. 위에 적지 않은 값이라면 무엇이든 매칭돼요. match는 가능한 모든 값을 처리해야 해요. 그래서 보통 “그 밖의 모든 경우”를 처리하려고 _를 둬요.

|를 쓰면 여러 값을 갈래(arm) 하나로 묶을 수 있어요.

fn main() {
let day = 6;
match day {
1 | 2 | 3 | 4 | 5 => println!("평일"),
6 | 7 => println!("주말"),
_ => println!("올바른 요일이 아니에요"),
}
}
▶ 직접 실행해 보기

숫자라면 ..=로 범위 전체를 매칭할 수 있어요.

fn main() {
let score = 85;
match score {
90..=100 => println!("A"),
75..=89 => println!("B"),
60..=74 => println!("C"),
_ => println!("F"),
}
}
▶ 직접 실행해 보기

if와 마찬가지로 match도 값을 만들어 낼 수 있어요.

fn main() {
let day = 6;
let kind = match day {
1 | 2 | 3 | 4 | 5 => "평일",
6 | 7 => "주말",
_ => "알 수 없음",
};
println!("{kind}");
}
▶ 직접 실행해 보기

match는 숫자에만 쓰는 게 아니에요. 문자열 슬라이스(&str)와 문자에도 쓸 수 있고, 'a'..='z' 같은 문자 범위도 숫자 범위와 똑같이 동작해요.

fn main() {
let command = "stop";
match command {
"start" => println!("시작할게요..."),
"stop" | "quit" => println!("멈출게요."),
_ => println!("알 수 없는 명령: {command}"),
}
let letter = 'k';
let kind = match letter {
'a' | 'e' | 'i' | 'o' | 'u' => "모음",
'a'..='z' => "자음",
'0'..='9' => "숫자",
_ => "기타",
};
println!("'{letter}'의 종류: {kind}");
let name = String::from("Ferris");
match name.as_str() {
"Ferris" => println!("안녕, 게 친구!"),
_ => println!("안녕, {name}!"),
}
}

실행 결과:

멈출게요.
'k'의 종류: 자음
안녕, 게 친구!
▶ 직접 실행해 보기

여기서 눈여겨볼 점이 두 가지 있어요.

  • 순서가 중요해요. 'a'는 'a'..='z' 안에도 들어 있지만, 모음 갈래가 먼저 오기 때문에 모음은 자음 갈래까지 내려가지 않아요. Rust는 항상 맞는 갈래 중 첫 번째를 골라요.
  • String에는 .as_str()이 필요해요. "Ferris"처럼 따옴표로 적은 텍스트는 &str이에요. String을 그대로 이 패턴들과 match하면 Rust는 error[E0308]: mismatched types ... expected String, found &str라는 에러를 내고 멈춰요. 타입이 맞지 않는다는 뜻이에요. .as_str()을 호출하면 String을 &str로 바라보는 값을 얻을 수 있어서, 패턴과 비교할 수 있게 돼요. (두 타입의 차이는 문자열 페이지를 보세요.)

매칭은 대소문자를 구분해요. "Stop"은 "stop"과 매칭되지 않아요. 사용자가 입력한 텍스트라면 input.to_lowercase().as_str()으로 매칭하는 방법을 많이 써요.

참/거짓만 확인하는 간단한 경우라면 둘 다 괜찮아요. 하지만 가능한 경우가 두세 개보다 많아지면 match가 유리해져요. 길게 이어지는 else if보다 읽기 좋고, 모든 경우를 처리했는지 컴파일러가 확인해 주거든요. 예/아니요로 끝나는 조건 하나라면 여전히 if/else가 알맞은 도구예요.

month에 숫자(2)를 저장하세요. match를 식으로 써서 그달이 며칠까지 있는지 계산하고(윤년은 무시해요), 2월은 28일까지 있어요를 출력하세요. 힌트: 1, 3, 5, 7, 8, 10, 12월은 31일까지 있어요. |로 갈래 하나에 묶어 보세요.

정답 보기
fn main() {
let month = 2;
let days = match month {
1 | 3 | 5 | 7 | 8 | 10 | 12 => 31,
4 | 6 | 9 | 11 => 30,
2 => 28,
_ => 0, // 존재하지 않는 달
};
println!("{month}월은 {days}일까지 있어요");
}

실행 결과:

2월은 28일까지 있어요
▶ 직접 실행해 보기

웹 서버는 숫자로 응답해요. code를 짧은 의미로 바꿔 보세요. 200은 "OK", 404는 "Not Found", 그 밖의 400499는 "클라이언트 에러", 500599는 "서버 에러", 나머지는 모두 "알 수 없음"이에요. 503으로 시험해 보세요. (OK와 Not Found는 HTTP 표준에 정해진 문구라서 영어 그대로 두었어요.) 힌트: 정확히 404만 매칭하는 갈래를 400..=499 범위보다 위에 두세요. 그러지 않으면 그 갈래까지 절대 도달하지 못해요.

정답 보기
fn main() {
let code = 503;
let meaning = match code {
200 => "OK",
404 => "Not Found", // 400..=499 범위보다 먼저 와야 해요
400..=499 => "클라이언트 에러",
500..=599 => "서버 에러",
_ => "알 수 없음",
};
println!("{code}: {meaning}");
}

실행 결과:

503: 서버 에러
▶ 직접 실행해 보기
  • match는 값을 패턴들과 비교하고, 맞는 갈래를 실행해요.
  • 가능한 모든 값을 처리해야 해요. 나머지 전부는 _로 처리하세요.
  • |로 여러 값을 묶거나, ..=로 범위를 매칭할 수 있어요.
  • &str 텍스트와 char 값도 매칭할 수 있어요. String을 매칭할 때는 .as_str()을 쓰세요.
  • match도 if처럼 값을 만드는 식으로 쓸 수 있어요.

이해도 점검

1. match 식에서 _ 패턴은 어떤 역할을 하나요?

2. match 식이 가능한 모든 값을 처리하지 않고 _ 갈래도 없으면 어떻게 되나요?

3. 1, 2, 3처럼 여러 값을 갈래 하나에서 매칭하려면 어떻게 쓰나요?

점수: 0 / 3