IP 주소는 현재 4, 6 중 한 버전의 주소를 갖고 있다. 이러한 경우 enum
값은 열거된 데이터 중 하나가 될 수 있기 때문에 enum
을 사용하기에 적절하다.
enum IpAddrKind {
V4,
V6,
}
IpAddrKind
의 두 개의 variants에 대한 인스턴스를 만들 수 있다. 단,
identifier(식별자)에 의해 namespace가 생기며 double colon (::)를 사용하여 구분할 수 있다.
let four = IpAddrKind::V4;
let six = IpAddrKind::V6;
data의 타입을 정확히 알 수 없을 때에 유용하다. 추상화의 장점과 비슷한 맥락이다.
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"),
};
enum
의 각 variant의 instance를 만들 때도 함수의 형태를 활용해 보다 쉽게 정의할 수 있다. IpAddr::V4()
는 String
을 인자로 받아 IpAddr
타입의 instance를 반환해준다. 또한, enum
의 요소의 데이터 타입은 string, struct, u32 심지어 enum
까지 가능하고, 요소의 타입이 달라도 된다.
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
impl Message {
fn call(&self) {
// method body would be defined here
}
}
let m = Message::Write(String::from("hello"));
m.call();
Option
은 standard library에서 정의한 enum
이다. Option
타입은 많이 사용되는데, 값이 있거나 없을 수도 있는 아주 흔한 상황을 나타내기 때문이다. type system 관점에서 컴파일러가 모든 경우를 처리했는지 확인할 수 있게 된다. Rust에서는 null이 존재하지 않는다. 하지만 특정 구현에 따라 값의 존재 혹은 부재를 표현할 수 있는 enum이 바로 Option<T>
이다.
enum Option<T> {
None,
Some(T),
}
let some_number = Some(5);
let some_char = Some('e');
let absent_number: Option<i32> = None;
some_number
의 타입은 Option<i32>
이다. some_char
는 Option<char>
이다. absent_number
를 정의할 때, 컴파일러는 None
만 보고는 Some
의 타입을 추론할 수 없기 때문에 T
타입을 명시해줘야 한다.
let x: i8 = 5;
let y: Option<i8> = Some(5);
let sum = x + y; // Compile error: cannot add i8 to Option<i8>
위 코드는 컴파일 에러가 난다. 결국 Option<T>
를 사용하기 위해선 T
로 형태 변환을 해줘야 하고, 값이 없는 경우 여기서 걸러지게 된다. 실제로 개발할 때는 None
값을 위해서 실행되는 코드가 필요하기도 하다.