핵심
struct가 *"여러 필드를 동시에 가진다"*라면, enum은 "여러 후보 중 정확히 하나다". type theory에서는 이를 product type vs sum type이라 부르고, 둘이 모여 ADT(algebraic data type)를 이룬다.
struct Rectangle { width: u32, height: u32 } // width AND height (product)
enum IpAddrKind { V4, V6 } // V4 OR V6 (sum)
이름의 유래는 가능한 값의 개수다. struct가 가질 수 있는 값의 수는 (필드들의 가능한 값 수의) 곱(product), enum은 합(sum)이다. 이 차이가 모든 특징의 출발점.
가장 단순한 형태
enum IpAddrKind {
V4,
V6,
}
let four = IpAddrKind::V4;
let six = IpAddrKind::V6;
fn route(ip_kind: IpAddrKind) { /* ... */ }
이 단계까지는 다른 언어 enum과 비슷하다. variant는 enum 이름 아래로 namespacing되고(IpAddrKind::V4), 둘 다 같은 타입이라 한 함수가 양쪽을 모두 받는다. 다만 C enum이 정수 상수 alias인 데 비해, Rust enum은 이 단계부터 진짜 별개 타입이라 정수와 호환되지 않는다.
variant에 데이터 붙이기 — 결정적 차이
enum IpAddr {
V4(String),
V6(String),
}
let home = IpAddr::V4(String::from("127.0.0.1"));
let loopback = IpAddr::V6(String::from("::1"));
각 variant가 자기만의 데이터를 들고 다닌다. C enum, 보통의 Java enum, TypeScript enum에서는 불가능한 표현이다. 그리고 variant마다 데이터의 모양이 달라도 된다:
enum IpAddr {
V4(u8, u8, u8, u8),
V6(String),
}
variant 형태는 더 풍부해질 수 있다.
enum Message {
Quit, // 데이터 없음
Move { x: i32, y: i32 }, // struct 같은 명명 필드
Write(String), // 튜플 형태
ChangeColor(i32, i32, i32),
}
OOP 상속 계층의 환원
Java/C++에서 abstract class Shape + Circle/Rectangle/Triangle 자식 클래스로 표현하던 패턴이 Rust에선 enum 하나로 환원된다.
enum Shape {
Circle { radius: f64 },
Rectangle { width: f64, height: f64 },
Triangle { base: f64, height: f64 },
}
상속 없이도 동등한 표현력이 나오며, 함수가 Shape를 받으면 어떤 variant든 다 받는다. 분기 처리는 다음 절의 match가 visitor 패턴보다 훨씬 직접적으로 풀어준다. 직전 is-rust-object-oriented에서 본 "Rust가 상속을 거부하고 trait + enum으로 풀었다"의 후반부가 여기서 구체화된다.
enum에도 method 정의 가능
impl Message {
fn call(&self) { /* self는 호출된 variant */ }
}
let m = Message::Write(String::from("hello"));
m.call();
struct의 impl 블록 (what-are-methods-and-associated-functions)이 enum에도 동일하게 작동한다. self가 어떤 variant인지를 method 안에서 match로 분기하는 식으로 사용한다.
자동으로 생기는 생성자 함수
variant 이름이 자동으로 함수처럼 작동한다.
let f: fn(String) -> IpAddr = IpAddr::V4;
let v = f(String::from("...")); // IpAddr::V4("...")와 동일
iter().map(IpAddr::V4) 같은 함수형 패턴에 자연스럽게 들어맞는다.
다른 언어와의 매칭
| 언어 | sum type 표현 |
|---|---|
| C, 옛 Java, TypeScript | enum (named ints) — variant가 데이터 못 가짐 |
| Java 17+ | sealed class + record — Rust enum과 거의 동등 |
| Swift | enum (associated values) — Rust와 동일 |
| Kotlin | sealed class — Rust enum과 동등 |
| Haskell, ML | `data IpAddr = V4 String |
| C++ | std::variant<T, U, ...> — 비슷하지만 문법이 어색 |
Rust enum은 ML/Haskell 계열 ADT를 시스템 언어 문법으로 가져온 형태이며, 2010년대 이후 거의 모든 신규 언어가 이 패턴을 채택했다. Java 17의 sealed class 도입도 같은 흐름.
결론
Rust enum은 단순한 named integer constants가 아니라 각 variant가 서로 다른 모양의 데이터를 들고 다닐 수 있는 sum type이다. struct와 짝을 이뤄 ADT를 구성하며, OOP에서 상속 계층으로 표현하던 도메인 모델링을 — 상속 없이 — 단일 타입으로 깔끔하게 풀어낸다. variant 분기는 다음 절의 match로 처리한다.