Enum과 패턴 매칭 — Symbol과 case/when의 강력한 진화판
Rust의 enum은 데이터를 담을 수 있다 — Ruby에는 없는 개념
Ruby에서 상태를 표현할 때 Symbol을 쓴다. :active, :inactive, :pending. Rust의 enum이 이 역할을 한다.
enum Status {
Active,
Inactive,
Pending,
}
let s = Status::Active;
여기까지는 Ruby의 Symbol과 비슷하다. 근데 Rust enum의 진짜 힘은 각 변형에 데이터를 담을 수 있다는 것이다.
데이터를 담는 enum
enum Message {
Quit, // 데이터 없음
Text(String), // String 하나
Move { x: i32, y: i32 }, // 이름 있는 필드
Color(u8, u8, u8), // 여러 값
}
let msg = Message::Text(String::from("hello"));
let pos = Message::Move { x: 10, y: 20 };
Ruby에서 이걸 하려면 클래스 상속이나 Hash를 써야 한다. Rust에서는 enum 하나로 "이 타입은 이런 형태들 중 하나다"를 표현한다.
match — 강제 완전 매칭
Ruby의 case/when에 대응하지만, 모든 경우를 처리해야 한다.
# Ruby — else 없어도 OK
case status
when :active then "활성"
when :inactive then "비활성"
# :pending을 빠뜨려도 에러 없음
end
// Rust — 빠뜨리면 컴파일 에러
match status {
Status::Active => "활성",
Status::Inactive => "비활성",
Status::Pending => "대기", // 빠뜨리면 error[E0004]
}
이게 왜 좋은가? 나중에 enum에 새 변형을 추가하면, match를 쓴 모든 곳에서 컴파일 에러가 난다. 처리 안 한 케이스를 절대 놓치지 않는다.
match에서 데이터 추출
match msg {
Message::Quit => println!("종료"),
Message::Text(text) => println!("텍스트: {}", text),
Message::Move { x, y } => println!("이동: ({}, {})", x, y),
Message::Color(r, g, b) => println!("색: {},{},{}", r, g, b),
}
enum 안에 담긴 데이터를 꺼내면서 동시에 분기한다. Ruby에서 case/when으로 타입 체크하고 데이터를 꺼내는 걸 한 번에 하는 셈.
if let — 하나만 체크할 때
match가 모든 경우를 처리하는 반면, 하나만 확인하고 싶을 때는 if let을 쓴다.
if let Message::Text(text) = msg {
println!("텍스트: {}", text);
}
// 나머지는 무시
Ruby의 if status == :active 같은 느낌인데, 데이터 추출까지 동시에 한다.
Option과 Result도 enum이다
Option<T>는 Some(T) 또는 None, Result<T, E>는 Ok(T) 또는 Err(E). 둘 다 enum이다. Rust의 에러 처리가 enum 위에 세워져 있다는 뜻이다.
핵심 포인트
enum은 Ruby의 Symbol 그룹 + 데이터를 담을 수 있는 진화판
match는 case/when의 강화판 — 모든 케이스를 빠짐없이 처리
match에서 데이터 추출과 분기를 동시에 한다
Option, Result도 enum — Rust 에러 처리의 기반
장점
- ✓ enum에 변형을 추가하면 처리 안 한 곳을 컴파일러가 전부 찾아준다
- ✓ 데이터를 담는 enum은 Ruby의 클래스 상속보다 간결하다
단점
- ✗ Ruby의 Symbol처럼 가볍게 쓰기엔 보일러플레이트가 많다
- ✗ match의 강제 완전 매칭이 처음엔 귀찮게 느껴진다