열거형 (enumerations, enums) 이란?
하나의 타입이 가질 수 있는 값의 경우의 수가 제한적일 때 사용할 수 있다.
ex) 손가락이라는 타입은 일반적으로 '엄지', '검지', '중지', '약지', '새끼' 중에 하나이다.
러스트에는 Option
, match
, if let
과 같은 키워드를 통해 열거형을 효과적으로 표현할 수 있다.
열거형은 많은 언어에 존재하지만, 각 언어마다 열거형으로 할 수 있는 것들이 다르다.
러스트의 열거형은F#
,OCaml
,Haskell
과 같은 함수형 언어의 대수 데이터 타입과 비슷하다.
열거형이 적절한 경우
variants
라 불리는 한정적 경우의 수 중 하나만 될 수 있을 때 적절하다.- IP 주소는
v4
혹은v6
라는 두가지 선택지만 존재한다.- 이 둘 중 하나여야 하며, 다른 경우는 없다.
열거형 코드로 정의하기
enum IpAddrKind {
V4,
V6,
}
enum
키워드를 사용하여 열거형을 정의할 수 있다.enum 이름 { }
형태에서 대괄호 내에 열거형variants
를 정의할 수 있다.
열거형을 이용한 변수 정의
let four = IpAddrKind::V4;
let six = IpAddrKind::V6;
열거형의 요소를 변수에 할당하려면 열거형::variant
형태로 작성하면 된다.
구조체와의 비교, 어떤 때 열거형이 더 적합한가?
IP 주소를 체계적으로 저장하고 싶다고 가정하자.
구조체를 이용했을 때
enum IpAddrKind {
V4,
V6,
}
struct IpAddr {
kind: IpAddrKind,
address: String,
}
let home = IpAddr {
kind: IpAddrKind::V4,
address: String::from("127.0.0.1"),
};
let loopback = IpAddr {
kind: IpAddrKind::V6,
address: String::from("::1"),
};
IpAddrKind
로 열거형 타입을 만든 뒤IpAddr
구조체의 프로퍼티 타입으로 사용하여 IP 주소에 대한 정보를 저장했다.- 약간 장황하긴 해도, 데이터를 저장하는데 무리는 없다.
열거형을 이용했을 때
enum IpAddr {
V4(String),
V6(String),
}
let home = IpAddr::V4(String::from("127.0.0.1"));
let loopback = IpAddr::V6(String::from("::1"));
- 간결하면서도 코드를 읽는 입장에서 동일한 의미로 해석될 수 있는 코드를 작성할 수 있다.
열거형의 장점: 종류별로 다른 타입의 값을 가질 때
enum IpAddr {
V4(u8, u8, u8, u8),
V6(String),
}
- 구조체로 구성했으면 복잡했을 수도 있는 부분이지만, 열거형으로 표현하면 간단하다.
표준 라이브러리의 용례
struct Ipv4Addr {
// details elided
}
struct Ipv6Addr {
// details elided
}
enum IpAddr {
V4(Ipv4Addr),
V6(Ipv6Addr),
}
- 각각 다른 타입에 의해 초기화될 수 있도록 지정했다.
- 러스트의 열거형은 다른 열거형마저 포함할 수 있다.
열거형의 좋은 사용 예시
enum Message {
Quit,
Move {x: i32, y: i32},
Write(String),
ChangeColor(i32, i32, i32),
}
- 열거형
Message
는Message
가 가질 수 있는variants
들을 훌륭하게 표현하고 있다.
열거형을 다시 구조체로 나타내보기
struct QuitMessage; // 유닛 구조체
struct MoveMessage {
x: i32,
y: i32,
}
struct WriteMessage(String); // 튜플 구조체
struct ChangeColorMessage(i32, i32, i32); // 튜플 구조체
Message
열거형은 사실 여러개의 구조체가 뭉쳐있는 것과 동일한 형태였다.
열거형에 메서드 구현하기
impl Message {
fn call(&self) {
// 메소드 내용은 여기 정의할 수 있습니다.
}
}
let m = Message::Write(String::from("hello"));
m.call();
Option
열거형
Null
특성 에 대하여
- 값이 있을수도 없을수도 있는 상황을 나타낸다.
- Rust 에는
Null
특성이 존재하지 않는다.- 타 언어에 있는
Null
특성은Null
인 값을Null
이 아닌 것처럼 사용했을 때 오류를 만들어내며, 종종 추적하기 어려운 오류가 만들어지기도 한다.
- 타 언어에 있는
Rust 의 Option
enum Option<T> {
Some(T),
None,
}
- 이 타입과
if-let
혹은match
와 같은 예약어를 이용해 컴파일러가 모든 경우를 처리했는지 검증할 수 있게 된다.- 버그를 방지할 수 있다.
- 다른 언어에서도 흔하게 보이는 패턴이다.
- 기본적으로 언어에 포함되기 때문에 명시적으로 가져오지 않아도 사용이 가능하다.
Option
의 열거형인Some
과None
은 앞에Option::
을 붙이지 않아도 바로 사용 가능하다.<T>
는 제너릭 파라미터이다.
Option
열거형 초기화하기
let some_number = Some(5);
let some_string = Some("a string");
let absent_number: Option<i32> = None;
Option<T>
는 T
와 같은 타입이 아니다.
let x: i8 = 5;
let y: Option<i8> = Some(5);
let sum = x + y;
- 위 코드는 당연히 에러가 나게 된다.
error[E0277]: the trait bound `i8: std::ops::Add<std::option::Option<i8>>` is
not satisfied
-->
|
7 | let sum = x + y;
| ^^^^^
|
- 사용을 위해
Option<T>
를T
로 변환시키는 작업이 필요하다.
러스트가 Option
을 통해 의도한 것
Option<T>
타입이 아닌 모든 타입은null
이 아님을 안전하게 가정할 수 있다.- 타 언어에서처럼
null
의 남용을 막는다. - 어떤 값이
Option<T>
라면Option<T>
의 각variants
경우의 수에 따른 처리를 모두 구현해야 한다.- 이 때
if-let
혹은match
가 매우 유용하다.
- 이 때
반응형
'러스트 (Rust)' 카테고리의 다른 글
러스트 (Rust) 열거형 3 - if-let 키워드 (0) | 2022.12.03 |
---|---|
러스트 (Rust) 열거형 2 - match 키워드 (0) | 2022.12.03 |
러스트 (Rust) 메서드 (Method) 문법 (0) | 2022.11.03 |
러스트 (Rust) 구조체를 이용한 리팩토링 연습 (0) | 2022.11.03 |
러스트 (Rust) 구조체 정의하고 생성하는 방법 (0) | 2022.11.03 |