열거형 enum
단순한 enum 타입
enum 키워드를 사용하여 정의하는 열거형 타입은 한 타입의 종류(variant)를 나열할 때 흔히 사용된다. 예를 들어, 아래 예제와 같이 우선순위(Priority)의 종류를 Low, Medium, High 등으로 나누어 표시하기 위해 enum 타입을 정의할 수 있다.
#[derive(Debug)] enum Priority { Low, Medium, High } fn main() { let urgent = Priority::High; dbg!(urgent); }
enum 타입에서 각 종류들을 지정하기 위해서는 :: 을 사용하는데, 예를 들어, 위의 urgent 변수는 Priority::High으로 설정되었다. 위의 enum 타입에 #[derive(Debug)] 을 지정하였는데, 이는 반드시 필요한 것은 아니며, 예제에서 디버그 출력(dbg!)을 사용하기 위한 것이다.
Rust의 enum 타입
Rust는 (다른 프로그래밍 언어와 달리) enum 타입으로 보다 많은 기능을 사용할 수 있게 한다. Rust에서 enum 타입은 단순히 타입의 종류(variant)를 표시하는 것을 넘어, 각 종류에 임의의 데이타를 지정할 수 있는 기능을 제공한다.
아래 예제는 LogType의 종류로서 Info, Warning, Error 등을 정의한 것인데, 이들 각 종류별로 관련된 데이타를 받아들일 수 있도록 파라미터를 지정하였다. Info와 Warning에는 메시지 문자열 하나를 받아들이고, Error에는 에러코드(i32)와 에러 메시지(String) 파라미터들을 받아들이고 있다.
#[derive(Debug)] enum LogType { Info(String), //(infoMsg) Warning(String), //(warningMsg) Error(i32, String) //(errorCode, errorMsg) } fn main() { // enum 타입에 파라미터 직접 사용 let log = LogType::Error(1201, String::from("Not found")); println!("{:?}", log); }
위의 LogType 열거형은 파라미터들을 Tuple로 지정(예: (i32, String))하였는데, Tuple 이외에도 각 종류별로 struct 등 여러 타입을 지정할 수도 있다. 예를 들어, 아래 예제처럼 Error를 구조체 타입으로 정의하여 사용할 수 있다.
#[derive(Debug)] enum LogType { None, Info(String), Warning(String), Error {code: i32, msg: String, caller: String } //구조체로 정의 } fn main() { // 구조체값 설정 let log = LogType::Error { code: 1201, msg: String::from("Not found"), caller: String::from("main") }; println!("{:#?}", log); }
Option 열거형 타입
Rust의 표준 라이브러리에는 다음과 같은 Option 열거형 타입이 정의되어 있다. Rust는 다른 프로그래밍 언어와 달리 null 타입을 지원하지 않고 있는데, 대신 아래와 같은 Option 타입을 활용하여 value가 있는지 없는지를 컴파일 타임에 체크하고 있다. Rust에서 어떤 변수가 Option 타입이 아니라면 그 변수는 항상 값을 갖는다고 볼 수 있고, 만약 (null과 같이) 값을 갖지 않을 수 있다면 Option을 사용하여 이를 핸들링해 주어야 한다.
enum Option<T> { None, Some(T), }
Option 열거형은 데이타가 없다는 것을 의미하는 None과 어떤 데이타가 있다는 것을 의미하는 Some으로 나뉘어 지고, Some에 들어갈 데이타 타입을 제네릭(generci)타입 T로 지정하고 있다.
Option 열거형을 사용하기 위해서는 다른 열거형과 마찬가지로 Option::None 혹은 Option::Some(값)를 사용하면 된다. 다만, Option 열거형이 매우 널리 사용되기 때문에 Option:: 을 생략하고 None 혹은 Some(값) 등으로 간략히 표현할 수 있다.
fn main() { // Option::None 지정 let no_index: Option<i32> = Option::None; // Option::Some 지정 let index: Option<i32> = Option::Some(1); // Option:: 생략 표현 let no_index: Option<i32> = None; let index = Some(1); println!("{:?}, {:?}", no_index, index); }
enum 열거형의 각 variant별로 어떤 처리를 수행하기 위해 일반적으로 다음 아티클에 소개하는 match expression을 자주 사용한다.