5.12. 열거형 (Enums) - 100%
Rust의 enum
은 여러 개의 변종(Variant) 중 하나의 데이터를 표현하는 타입입니다.
enum Message {
Quit,
ChangeColor(i32, i32, i32),
Move { x: i32, y: i32 },
Write(String),
}
각각의 변종은 자신과 연관된 데이터를 선택적으로 가질 수 있습니다. 변종을 정의하는 데 사용되는 문법은 구조체를 정의하는 데 사용되는 문법과 유사합니다: (단위 유사 구조체처럼) 데이터를 가지지 않는 변종을 정의할 수도 있고, 데이터에 이름을 붙여 정의할 수도 있고, (튜플 구조체처럼) 이름 없는 데이터를 정의할 수도 있습니다. 그러나 분리된 구조체 정의와는 달리 enum
은 한 가지의 타입만을 갖습니다. enum의 값은 변종 중 하나와 대응되면 됩니다. 이러한 연유로 종종 enum은 'sum type'이라 불리기도 합니다: enum의 가능한 값들의 집합이 각각의 변종의 가능한 값의 집합의 합이기 때문입니다.
각각의 변종을 사용하기 위해서 ::
문법을 사용합니다: 변종은 enum
범위 그 자체에 귀속되어 있습니다. 이는 아래 두 가지를 가능하게 합니다.
# enum Message {
# Move { x: i32, y: i32 },
# }
let x: Message = Message::Move { x: 3, y: 4 };
enum BoardGameTurn {
Move { squares: i32 },
Pass,
}
let y: BoardGameTurn = BoardGameTurn::Move { squares: 1 };
두 변종은 Move
라는 이름은 같으나, enum 범위에 귀속되어 있으므로 충돌 없이 사용할 수 있습니다.
enum 타입의 값은 변종에 연관된 데이터와 더불어 어느 변종인지에 관한 정보를 포함하고 있습니다. 데이터가 타입이 무엇인지를 말해주는 'tag'를 포함하고 있으므로 이것을 때때로 'tagged union'이라 부릅니다. 컴파일러는 이 정보를 이용하여 enum 내에 있는 데이터에 안전하게 접근하게끔 만듭니다. 예로 값이 변종 중 하나인 것처럼 가정하여 단순하게 값을 해체(destructure)하려 시도할 수 없습니다:
fn process_color_change(msg: Message) {
let Message::ChangeColor(r, g, b) = msg; // compile-time error
}
위와 같은 연산을 지원하지 않는 것이 상당히 제한적이라고 보이지만 충분히 극복할 수 있는 제한입니다. 이를 극복하는데 두 가지 방법이 있습니다: 우리 스스로 동등성(equality)을 구현하는 방법과 다음 부문에서 배우게 될 <code>패턴 정합</code>(pattern match) 을 이용하는 방법입니다. 동등성을 구현하는 데 있어 아직은 Rust를 잘 모르지만, <code>트레잇</code> 부문에서 알게 될 겁니다.
함수형 생성자 (Constructors as functions)
enum의 생성자는 함수처럼 사용됩니다. 에:
# enum Message {
# Write(String),
# }
let m = Message::Write("Hello, world".to_string());
위는 아래와 같음
# enum Message {
# Write(String),
# }
fn foo(x: String) -> Message {
Message::Write(x)
}
let x = foo("Hello, world".to_string());
이것은 우리에게 지금 당장 유용하지는 않습니다. 그러나 <code>클로저</code>를 배우게 될 때, 함수를 인자로 하여 다른 함수로 전달하는 것에 관해 말할 것입니다. 예로 <code>반복자</code>를 가지고서 String
벡터를 Message::Write
벡터로 전환하는데 함수를 다른 함수로 전달할 수 있습니다:
# enum Message {
# Write(String),
# }
let v = vec!["Hello".to_string(), "World".to_string()];
let v1: Vec<Message> = v.into_iter().map(Message::Write).collect();