데이터 배리언트 매칭
Coddy Rust 여정의 Object Oriented Programming 섹션에 포함된 레슨 — 61개 중 16번째.
이전 레슨에서 여러분은 다양한 변형(variants)을 처리하기 위해 열거형(enum) 메서드 내부에서 match를 사용했습니다. 이제 match가 실제로 해당 변형 내부에 저장된 데이터를 어떻게 추출하는지에 집중해 보겠습니다. 이 기술을 구조 분해(destructuring)라고 합니다.
데이터를 보유한 열거형 변형(enum variant)에 대해 매칭할 때, 패턴 내에서 해당 데이터를 변수에 바로 바인딩할 수 있습니다:
enum Event {
Click { x: i32, y: i32 },
KeyPress(char),
Resize(u32, u32),
}
fn handle_event(event: Event) {
match event {
Event::Click { x, y } => {
println!("Clicked at ({}, {})", x, y);
}
Event::KeyPress(key) => {
println!("Key pressed: {}", key);
}
Event::Resize(width, height) => {
println!("Resized to {}x{}", width, height);
}
}
}
각 암(arm)은 배리언트의 구조에 따라 데이터를 다르게 추출합니다. 구조체 형태의 배리언트의 경우, { field_name }을 사용하여 이름이 있는 필드를 추출합니다. 튜플 형태의 배리언트의 경우, (width, height)와 같은 위치 기반 변수를 사용합니다.
선택한 변수 이름은 해당 매치 암(match arm) 내부에서 사용할 수 있게 됩니다. 이는 어떤 배리언트(variant)인지 확인하는 것과 동시에 그 페이로드(payload)에 접근할 수 있기 때문에 매우 강력합니다.
컴파일러는 모든 변형(variant)을 처리하도록 보장하며, 추출된 값은 즉시 사용할 수 있어 별도의 단계가 필요하지 않습니다.
챌린지
쉬움다양한 유형의 사용자 명령을 처리하는 명령 프로세서를 만들어 보겠습니다. 각 명령은 서로 다른 데이터를 포함하며, 프로세서는 패턴 매칭을 사용하여 해당 데이터를 추출하고 적절한 응답을 생성합니다.
코드를 구성하기 위해 두 개의 파일을 생성합니다:
command.rs: 세 가지 변리언트(variant)를 가진 공개Command열거형(enum)을 정의합니다:Say— 단일String메시지를 보유합니다 (튜플 구문)Move— 이름이 있는 필드인direction(String)과steps(u32)를 보유합니다Calculate— 더할 숫자를 나타내는 두 개의i32값을 보유합니다 (튜플 구문)
match를 사용하여 각 변리언트를 구조 분해(destructure)하고 수행된 작업을 설명하는String을 반환하는execute메서드를 구현합니다.main.rs: 명령 모듈을 가져오고, 제공된 입력을 사용하여 각 명령 변리언트의 인스턴스를 하나씩 생성한 다음, 각각에 대해execute메서드를 호출하여 결과를 출력합니다.
execute 메서드는 각 변리언트를 구조 분해하여 데이터에 접근하고, 정확히 다음 형식으로 메시지를 반환해야 합니다:
Say의 경우:Saying: {message}Move의 경우:Moving {steps} steps {direction}Calculate의 경우:{a} + {b} = {sum}
출력은 각 명령의 결과를 별도의 줄에 표시해야 합니다:
Saying: {message}
Moving {steps} steps {direction}
{a} + {b} = {sum}예를 들어, say 메시지가 "Hello world"이고, "north" 방향으로 5 steps 이동하며, 10과 25를 계산하는 경우 출력은 다음과 같습니다:
Saying: Hello world
Moving 5 steps north
10 + 25 = 35say 메시지, 방향(direction), 걸음 수(steps), 그리고 계산할 두 개의 숫자까지 총 다섯 개의 입력을 받게 됩니다.
치트 시트
match를 사용한 패턴 매칭을 통해 열거형(enum) 변형을 구조 분해하고 해당 데이터를 패턴에서 직접 추출할 수 있습니다.
튜플 변형(tuple variants)의 경우, 위치 기반 변수를 사용합니다:
enum Event {
KeyPress(char),
Resize(u32, u32),
}
match event {
Event::KeyPress(key) => {
println!("Key: {}", key);
}
Event::Resize(width, height) => {
println!("{}x{}", width, height);
}
}구조체형 변형(struct-like variants)의 경우, 이름이 지정된 필드 구문을 사용합니다:
enum Event {
Click { x: i32, y: i32 },
}
match event {
Event::Click { x, y } => {
println!("Clicked at ({}, {})", x, y);
}
}추출된 변수는 각각의 match arm 내에서 사용할 수 있게 되며, 이를 통해 변형 타입을 확인하는 동시에 데이터에 접근할 수 있습니다.
직접 해보기
mod command;
use command::Command;
fn main() {
// 입력 읽기
let mut say_message = String::new();
std::io::stdin().read_line(&mut say_message).expect("Failed to read line");
let say_message = say_message.trim().to_string();
let mut direction = String::new();
std::io::stdin().read_line(&mut direction).expect("Failed to read line");
let direction = direction.trim().to_string();
let mut steps_input = String::new();
std::io::stdin().read_line(&mut steps_input).expect("Failed to read line");
let steps: u32 = steps_input.trim().parse().expect("Failed to parse steps");
let mut num1_input = String::new();
std::io::stdin().read_line(&mut num1_input).expect("Failed to read line");
let num1: i32 = num1_input.trim().parse().expect("Failed to parse num1");
let mut num2_input = String::new();
std::io::stdin().read_line(&mut num2_input).expect("Failed to read line");
let num2: i32 = num2_input.trim().parse().expect("Failed to parse num2");
// TODO: say_message를 사용하여 Say 명령을 생성하고 실행하기
// TODO: direction과 steps를 사용하여 Move 명령을 생성하고 실행하기
// TODO: num1과 num2를 사용하여 Calculate 명령을 생성하고 실행하기
// TODO: 각 명령 실행 결과를 출력하기
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.