프로그래밍Rust 개발자 (인프라/핵심 라이브러리)

Rust에서 match 연산자는 어떻게 작동합니까: exhaustiveness checking, 패턴 가드 및 중첩 패턴 매칭의 특징?

Hintsage AI 어시스턴트로 면접 통과

답변.

질문의 역사

Rust의 match 연산자는 함수형 언어에서 차용한 강력한 패턴 매칭 도구입니다. C/C++의 유사한 도구들과는 달리, Rust는 엄격한 완전성 검사(exhaustiveness checking)를 실시하며, 각 가능한 경우에 대해 분기 옵션이 필요합니다.

문제점

match와 관련된 오류는 대개 (예: enum) 타입의 모든 경우를 잘못 고려하거나, guard(분기에 대한 조건)를 잘못 처리하거나, 복잡한 중첩 구조와 관련이 있습니다. 잘못된 처리는 컴파일 단계에서 오류를 발생시키거나, 암묵적으로 잘못된 논리로 이어질 수 있습니다.

해결책

  • Exhaustiveness checking은 어떤 경우도 생략할 수 없으며, 컴파일러는 각 경우에 대한 분기를 추가하도록 강제합니다 (또는 catch-all _).
  • 패턴 가드는 match 분기 뒤에 추가 조건(if)을 더합니다.
  • 중첩 패턴 매칭을 통해 복잡한 중첩 enum 또는 튜플을 하나의 match 구문으로 풀어낼 수 있습니다.

코드 예시:

enum Shape { Circle(f64), Rectangle { width: f64, height: f64 }, } fn print_area(s: Shape) { match s { Shape::Circle(r) if r > 0.0 => println!("area = {}", 3.14 * r * r), Shape::Rectangle { width, height } if width > 0.0 && height > 0.0 => println!("area = {}", width * height), _ => println!("invalid shape"), } }

주요 특징:

  • 패턴 매칭의 완전성 검사
  • 필터링을 위한 가드 표현식 사용 가능
  • 중첩 구조와의 패턴 매칭 및 구조 분해 작업

함정 질문들.

match에서 분기의 순서가 실행에 영향을 미치나요?

네, 분기의 순서가 중요합니다: 첫 번째 일치 항목이 발견되면 이후의 분기는 검사되지 않습니다. 이는 패턴 가드와 함께 특히 중요합니다. 이전에 guard가 있는 분기가 있으면 catch-all 이전에 값을 가로챌 수 있습니다.

enum에 대해 match 시 catch-all (_)이 필수인가요?

아니요, 모든 경우를 명시적으로 처리했다면 필요하지 않습니다 (그리고 타입의 선언이 시간이 지남에 따라 변경되지 않는다면). 그러나 추가 값이 나타날 수 있는 타입을 다룰 때나 모든 분기를 명시적으로 처리하고 싶지 않을 때는 catch-all이 필요합니다.

하나의 match 분기에서 여러 패턴(alternatives)을 사용할 수 있나요?

네, 수직선(|)을 통해 패턴을 결합할 수 있습니다:

match x { 1 | 2 | 3 => println!("one, two or three"), _ => println!("something else"), }

일반적인 오류 및 안티 패턴

  • catch-all 없이 모든 enum 경우를 처리하는 것을 잊기
  • 특정 분기보다 catch-all _을 너무 일찍 사용하기
  • 가드가 있는 분기의 순서를 무시하기

실제 사례

부정적 사례

개발자가 catch-all을 처음에 작성한 match를 작성했지만, 특정 경우를 올바르게 처리하지 못했습니다.

장점:

프로그램이 컴파일됩니다.

단점:

특정 로직이 결코 작동하지 않으며, 코드의 일부가 다루어지지 않습니다.

긍정적 사례

enum의 모든 경우를 명시적으로 처리하며, catch-all은 마지막 분기로 두고 다양한 경우에 대한 개별 가드를 둡니다.

장점:

예측 가능성이 있으며, 컴파일러가 경우를 잊지 않도록 도와주며, 타입을 쉽게 확장할 수 있습니다.

단점:

많은 경우가 있을 경우 추가 템플릿 코드가 필요합니다.