열거형은 일반적으로 상태(state)를 정의하기 위한 타입입니다. 하나의 타입에 여러 선택 가능한 값을 정의해두고 이 값들 중 하나를 선택할 수 있게 하기 위한 특수한 타입이지요. 러스트(Rust)도 열거형 타입을 지원하며 그 목적도 비슷합니다. 다만 각 값에 별도로 가질 수 있는 독립된 추가 값을 정의할 수 있다는 점이 독특한데 스위프트(Swift)의 열거형과도 비슷한 느낌을 받았습니다.
열거형(Enums)
러스트의 열거형은 아래와 같은 형식으로 정의할 수 있습니다.
enum Gender {
Male,
Female,
Other,
}
위 코드는 세 가지 값 즉 Male
, Female
, Other
라는 세 가지 값 중 하나를 가질 수 있는 Gender
라는 타입을 정의하는 예제입니다.
다른 언어에 비해 약간 특수한 점이 있다면 각 값 이름들은 대문자로 시작하는 것을 권장한다는 점이겠네요. 소문자로 시작하는 것을 권장하는 스위프트와는 약간 다릅니다. 물론 권장이기 때문에 꼭 따를 필요는 없습니다.
열거형의 사용 및 비교
사용 방법은 단순합니다. 열거형은 하나의 상태를 정의하기 위한 타입이기 때문에 바로 각 값 자체를 그대로 시용할 수 있습니다.
let gender = Gender::Female;
위 코드는 Gender
타입을 이용해 여성을 의미하는 성별을 특정 변수는 담는 예제입니다.
그렇다면 해당 변수가 어떤 상태인지 확인은 어떻게 할 수 있을까요? 대충 if
로 비교해보면 되지 않을까요?
if gender == Gender::Female { // ERROR
println!("Yeoja!");
}
불행히도 위 코드는 아래와 같은 빌드 오류를 냅니다.
binary operator
==
cannot be applied to typeGender
왜 이걸 못 하는지 이해가 안 되지만 안 된다니 어쩔 수가 없네요.
대신 if let
구문을 이용해 단순 상태 비교는 가능합니다. 아래와 같은 식이지요.
if let Gender::Female = gender {
println!("Yeoja!");
}
이렇게 하면 gender
가 Gender::Female
인 경우에 분기를 구현할 수 있습니다.
match를 이용한 비교
사실 if let
을 이용해 모든 값을 비교하는 것은 해당 열거형이 클수록 귀찮아질 수밖에 없습니다. 이럴 때는 match
를 이용해 분기하는 편이 편할 수 있습니다.
match gender {
Gender::Male => println!("Namja!"),
Gender::Female => println!("Yeoja!"),
_ => println!("Molla!")
}
match
는 switch-case문과 비슷하게 쓰입니다. match
에 대해서는 러스트의 제어구조 글에서도 언급하고 있으니 참고합시다.
별도의 값을 가질 수 있는 값(?)
열거형의 각 값은 자신만의 별도의 데이터를 가질 수 있습니다.
enum Gender {
Male,
Female,
Special(String),
Other,
}
위의 경우 Special
이라는 값은 별도의 문자열을 데이터로 가질 수 있습니다.
따라서 아래와 같이 값과 함께 데이터도 선언해서 변수에 담을 수 있습니다.
let gender = Gender::Special(String::from("Secret"));
이렇게 값에 데이터를 담으면 아래와 같은 식으로 match
시 꺼내올 수 있습니다.
match gender {
Gender::Special(name) => println!("{}", name),
_ => println!("Molla")
}
혹은 if let
을 통해서도 열거형 값의 데이터를 꺼내올 수 있습니다.
if let Gender::Special(name) = gender {
println!("{}", name);
}
이런 열거형 값의 데이터는 다양한 형식으로 표기할 수 있습니다. 하나 이상의 값을 가질 수도 있고, 튜플 형식이 아닌 이름 없는 구조체 형식으로 표현하는 것도 가능합니다.
enum Gender {
Male,
Female,
Special(String),
DNA { nth: i32, value: i32 },
All ( i32, i32 ),
Other,
}
사실상 전부 데이터는 구조체 형식으로 가진다고 볼 수도 있습니다. 구조체 중에서는 튜플 구조체라는 특수한 형태도 있으니깐요. 잘 모른다면 구조체에 관한 별도의 글을 참고할 수 있습니다.
구조체 형식의 데이터를 가지는 값은 앞서 언급된 방식이 아닌 구조체를 벗겨내는 방식으로 데이터를 읽어오는 것이 가능합니다.
let gender = Gender::DNA { nth: 10, value: -2 };
if let Gender::DNA { nth, value } = gender {
println!("nth {}, value {}", nth, value);
}
위의 예는 if let
을 사용했는데 당연하게도 match
에서도 비슷하게 활용이 가능합니다.
match gender {
Gender::DNA { nth, value } => println!("nth {}, value {}", nth, value),
_ => println!("Molla!")
}
뭘 쓰는 것은 개인의 마음이지만 대부분의 언어에서 열거형의 비교는 switch-case를 추천하는 만큼 러스트에서는 match를 사용하는 편이 좋을 것 같다고 생각됩니다.
맺음말
열거형 자체는 간단합니다. 단지 중요한 특징이 있다면 러스트로 코딩하는 도중 계속해서 만날 Option
과 Result
타입이 이 열거형으로 구현되어 있다는 점이겠네요. 이 두 타입은 러스트의 기본 타입들 글에서 언급하고 있으니 참고합시다. 어쨌거나 그만큼 기본기면서도 중요한 타입 정의 수단으로 볼 수 있습니다.
요즘 글을 쓰기 위한 짬을 내는 것이 힘들어지고 있습니다. 다음 업데이트는 늦어질 수 있다는 말이겠네요. 읽어주시는 모든 분들께 감사드립니다.
'기술적인 이야기 > 기타 개발' 카테고리의 다른 글
Python Exceptions 간략 정리 (579) | 2020.10.09 |
---|---|
Python 3.9 변경사항 간단 정리 (634) | 2020.10.06 |
flutter: 당신의 Xcode는 너무 구버전이예요! (818) | 2020.09.18 |
러스트의 가변성(Mutability) 이야기 (1040) | 2020.09.04 |
러스트의 미래는 어떻게 될까? (1053) | 2020.08.19 |
댓글