열거형과 Match로 대안 표현하기

RustBeginner
지금 연습하기

소개

구조체는 항상 함께 속하는 필드를 하나로 묶습니다. **열거형(enum)**은 여러 대안 중 하나가 될 수 있는 값을 나타내며, 각 대안은 서로 다른 데이터를 전달할 수 있습니다. Rust의 match 표현식은 이러한 대안을 명시적으로 처리합니다.

메시지 열거형을 완성하고, 한 변형에서 좌표를 구조 분해하며, 새 변형을 추가해 모델을 확장합니다. 메시지 프로세서가 실행되기 전에 가능한 모든 변형을 처리해야 하는 이유를 컴파일러가 직접 보여 줍니다.

데이터를 전달하는 변형 정의하기

이 단계에서는 하나의 Message 타입에 사용할 세 가지 대안 형태를 정의합니다.

준비된 프로젝트로 이동해 소스 파일을 엽니다.

cd /home/labex/project/message-processor
nano src/main.rs

열거형 선언에서는 중괄호 안에 **변형(variant)**을 나열합니다. 코드에서 변형을 생성할 때는 열거형 이름, ::, 변형 이름을 차례로 사용합니다. 같은 열거형에 속한 변형이라도 서로 다른 종류의 데이터를 전달할 수 있습니다.

Step 1 주석을 다음 내용으로 바꿉니다.

    Text(String),
    Move { x: i32, y: i32 },
    Stop,

Text(String)은 이름이 없는 값 하나를 전달합니다. Rust에서는 이를 **튜플 형태 변형(tuple-like variant)**이라고 합니다. Move { x, y }는 이름이 있는 필드를 전달하므로 **구조체 형태 변형(struct-like variant)**이라고 합니다. Stop은 추가 데이터를 전달하지 않으므로 **단위 변형(unit variant)**이라고 합니다. 처음부터 형식적인 이름을 외우기보다는 “이름 없는 값 하나, 이름이 있는 필드, 데이터 없음”이라는 형태를 기억하는 것이 더 중요합니다. 형태는 서로 다르지만, 생성된 모든 값의 타입은 하나의 Message입니다.

Ctrl+O를 눌러 저장하고, Enter를 누른 다음 Ctrl+X로 종료합니다. 준비된 프로세서를 확인하고 실행합니다.

cargo check
cargo run --quiet

출력은 다음과 같아야 합니다.

Text: Maintenance at 9
Move received
Stop requested

준비된 match는 각 변형에 대해 하나의 arm을 선택합니다. 각 arm은 pattern => behavior 형식이며, 짧은 arm 사이에는 쉼표를 사용합니다. Text(text) 패턴은 전달된 문자열에 이름을 붙입니다. Move arm도 x와 y에 이름을 붙인 다음, 이를 (x, y)로 임시 그룹화합니다. 여기서 괄호는 준비된 뼈대 코드에서만 사용하는 두 값 튜플을 만듭니다. 이 실습에서는 튜플 자체를 학습 목표로 다루지 않습니다. _coordinates처럼 이름이 _로 시작하면 이 임시 값을 사용하지 않는 것이 의도된 동작임을 Rust에 알립니다. 따라서 첫 실행에서 경고가 표시되지 않습니다. 다음 단계에서는 이 뼈대 코드를 바꾸고 두 좌표를 출력에 사용합니다.

구조 분해한 변형 데이터 사용하기

이 단계에서는 Move의 match arm을 바꿔 패턴으로 추출한 좌표가 출력에 나타나도록 합니다.

패턴은 자신이 일치하는 변형의 내부 데이터를 분해할 수 있습니다. 구조체 형태 변형에서 필드 이름을 작성하면 arm의 표현식에서 사용할 수 있는 바인딩이 만들어집니다.

소스 파일을 엽니다.

nano src/main.rs

Move arm 전체를 다음 내용으로 바꿉니다.

        Message::Move { x, y } => println!("Move to ({x}, {y})"),

Move 메시지가 들어오면 패턴은 저장된 필드를 지역 이름 x와 y에 바인딩합니다. 출력 표현식은 이 이름을 사용하므로 별도의 필드 접근이나 형 변환이 필요하지 않습니다.

저장하고 종료한 다음 실행합니다.

cargo run --quiet

이제 가운데 줄은 다음과 같아야 합니다.

Move to (3, -2)

음수인 y 값은 arm이 고정된 좌표를 출력한 것이 아니라 실제 변형이 전달한 데이터를 사용했다는 것을 보여 줍니다.

모든 경우를 처리하는 match 확장하기

이 단계에서는 Pause 메시지를 추가하고, 컴파일러의 모든 경우 처리 검사를 확인한 다음, 새 대안을 처리합니다.

match는 열거형의 가능한 모든 값을 처리해야 합니다. 따라서 변형을 추가하면 새 경우를 어떻게 처리할지 결정해야 하는 모든 match를 확인할 수 있습니다.

파일을 열고 Stop 바로 아래에 다음 변형을 추가합니다.

nano src/main.rs
    Pause(u32),

저장하고 종료한 다음 프로그램을 확인합니다. 이때 오류가 발생하는 것이 정상입니다.

cargo check

진단 메시지에서 다음 부분을 찾습니다.

error[E0004]: non-exhaustive patterns: `Message::Pause(_)` not covered

열거형에 이제 Pause 값도 포함될 수 있지만, 이를 처리할 arm이 없기 때문에 컴파일러는 match message를 가리킵니다.

소스 파일을 다시 엽니다. Stop arm 아래에 다음 arm을 추가합니다.

        Message::Pause(seconds) => println!("Pause for {seconds} seconds"),

그런 다음 main에 준비되어 있는 process(Message::Pause(5)); 줄에서 앞의 //를 제거합니다. 이 패턴은 전달된 u32 값에 지역 이름 seconds를 부여합니다.

저장하고 종료한 다음 확인하고 실행합니다.

cargo check
cargo run --quiet

전체 출력은 다음과 같아야 합니다.

Text: Maintenance at 9
Move to (3, -2)
Stop requested
Pause for 5 seconds

확인이 성공했다는 것은 match가 다시 모든 경우를 처리한다는 뜻입니다. 네 번째 줄은 새 arm이 Pause의 시간을 구조 분해했다는 것을 보여 줍니다.

요약

단위 변형, 튜플 형태 변형, 구조체 형태 변형을 정의했습니다. match 패턴으로 전달된 데이터를 구조 분해했으며, 새 대안을 추가한 뒤 컴파일러 오류 E0004를 사용해 모든 경우를 처리하도록 match를 확장했습니다.