티스토리 뷰
열거형
enum 키워드를 이용하여 작성한다.
구조체보다 유용하고 적절하게 사용될 상황이 있다.
예를들면 IP주소의 버전을 다룰때를 생각해봤을때, ipv4, ipv6가 있다.
모든 가능한 값들을 나열할 수 있으며 이를 열거라고 한다.
IP주소는 v4, v6 중 하나이며 동시에 두 버전이 될 수 없다. 이 경우 열거형이 적절하다.
열거형의 값은 variants 중 하나만 사용할 수 있다.
IP주소라는 같은 카테고리 내 다른버전일 경우에 대한 예시이다.
enum IpAddrKind{
V4, // variants
V6
}
IpAddrKind라는 열거형 타입이고, 각각 ipv4, ipv6를 나타낸다.
열거형 값
enum IpAddrKind{
V4, // variants
V6
}
pub fn run(){
let ipv4 = IpAddrKind::V4;
let ipv6 = IpAddrKind::V6;
route(ipv4);
route(ipv6);
}
fn route(ip_type : IpAddrKind){}
여기서 ipv4와 ipv6는 다르지만 동일한 타입이므로 IpAddrKind라는 타입에 모두 사용할 수 있다.
열거형의 각 variant에 데이터를 입력하는 방법이 있다.
enum IpAddrKind{
V4, // variants
V6
}
struct IpAddr{
kind: IpAddrKind,
address: String
}
pub fn run(){
let home = IpAddr{
kind: IpAddrKind::V4,
address: String::from("127.0.0.1")
};
let loopback = IpAddr{
kind: IpAddrKind::V6,
address: String::from("::1")
};
}
구조체를 정의하고 구조체의 필드에 선언한 열거형IpAddrKind를 정의하여 각각에 대한 정보를 address필드에 담는 방법이다.
하지만 더 간결하게 표현이 가능하다.
enum IpAddr{
V4(String),
V6(String)
}
pub fn run(){
let home = IpAddr::V4(String::from("127.0.0.1"));
let loopback = IpAddr::V6(String::from("::1"));
}
열거형의 각 variant에 직접 데이터를 붙임으로써, 구조체를 사용할 필요가 없다.
또한 각 variant는 다른 타입과, 다른 양의 연관된 데이터를 가질 수 있다.
enum IpAddr{
V4(u8, u8, u8, u8),
V6(String)
}
pub fn run(){
// let home = IpAddr::V4(String::from("127.0.0.1"));
let home = IpAddr::V4(127, 0, 0, 1);
let loopback = IpAddr::V6(String::from("::1"));
}
열거형 타입
열거형에는 어떤 종류의 데이터타입이라도 사용할 수 있다.
enum Message{
Quit, //연관된 데이터가 없음
Move{x: i32, y: i32}, //익명 구조체를 포함
Write(String), //하나의 String을 포함
ChnageColor(i32, i32, i32) //3개의 i32 포함
}
구조체의 경우 각기 다른 타입을 갖는 여러 구조체를 사용한다면, 어떤 한가지를 argument로 넘겨주는 함수를 정의하기 복잡해질 것이다. 열거형은 하나의 타입으로 가능하다.
메소드 정의
구조체와 유사하게 impl을 사용하여 method를 정의할 수 있다.
구조체와 마찬가지로 #[derive(Debug)]를 열거형 위에 작성해주어야 한다.
#[derive(Debug)]
enum Message{
Quit,
Move{x: i32, y: i32},
Write(String),
ChnageColor(i32, i32, i32)
}
pub fn run(){
let m = Message::Write(String::from("hello"));
let m2 = Message::Write(String::from("hi"));
m.call();
m.res(&m2);
}
impl Message{
fn call(&self){
println!("{:?}", self);
}
fn res(&self, other : &Message){
println!("m : [{:?}], m2 : [{:?}]", self, other);
}
}
Option열거형
Option타입은 값이 있을 수도, 없을 수도 있는 상황에서 사용할 숭 있다.
타입 시스템의 관점으로 컴파일러가 모든 경우를 처리했는지 확인할 수 있고, 버그를 방지할수 있다.
Option<T> 열거형이다.
enum Option<T> {
Some(T),
None,
}
Option열거형은 여전히 열거형이며, Some(T), None은 variante이다.
Some과 None 이외의 variante는 추가할 수 없고 제너릭 타입 파라미터인 T는 이름을 바꿔도 됨.
enum Option<T> {
Some(T),
None
}
pub fn run(){
let some_number = Some(5);
let some_string = Some("a string");
let absend_number: Option<i32> = Option::None;
}
Some이 아닌 None을 사용한다면 Option<T>가 어떤 타입을 가질지 rust에 알려줘야 한다. 그렇지 않으면 Some variant의 타입이 어떤건지 추론할 수 없기 때문이다.
Option<T>와 T는 다른타입이며, 컴파일러는 Option<T>값을 명확하게 유효한 값처럼 사용하지 못하도록 한다.
Option은 값이 Some에 해당할때도 일반 변수타입과 연산을 할수 없다는 의미다.
enum Option<T> {
Some(T),
None
}
pub fn run(){
let x: i8 = 5;
let y: Option<i8> = Option::Some(5);
let sum = x + y;
}
위의 코드는 컴파일 되지 않는다.
연산을 수행하기 전에 Option<T>를 T로 type casting해주어야 하며, None이라면 예외처리를 해줘야 한다.
이로 인해 Option<T>열거형은 null이 아닌 None을 가짐으로, null으로 부터 발생하는 수많은 오류로부터 안전할 수 있다.
null일 수 있는 값을 사용하기 위해 명시적으로 값의 type을 Option<T>로 만들어 주고, 그 다음 값을 사용할 때 null인 경우를 처리하면 된다. 값의 타입이 Option<T>가 아닌 모든 곳은 값이 null이 아니라고 안전히 가정할 수 있고, null의 사용을 제한하고, 코드의 안전성을 높일 수 있다.
이를 위하여 Option<T>와 Option<T>에 대한 Method들에 익숙해지자.
공식문서 : doc.rust-lang.org/std/option/enum.Option.html
전체 코드
enum IpAddrKind{
V4, // variants
V6
}
// struct IpAddr{
// kind: IpAddrKind,
// address: String
// }
enum IpAddr{
V4(u8, u8, u8, u8),
V6(String)
}
#[derive(Debug)]
enum Message{
Quit,
Move{x: i32, y: i32},
Write(String),
ChnageColor(i32, i32, i32)
}
#[derive(Debug)]
enum Option<T> {
Some(T),
None
}
pub fn run(){
let ipv4 = IpAddrKind::V4;
let ipv6 = IpAddrKind::V6;
route(ipv4);
route(ipv6);
// let home = IpAddr{
// kind: IpAddrKind::V4,
// address: String::from("127.0.0.1")
// };
// let loopback = IpAddr{
// kind: IpAddrKind::V6,
// address: String::from("::1")
// };
// let home = IpAddr::V4(String::from("127.0.0.1"));
let home = IpAddr::V4(127, 0, 0, 1);
let loopback = IpAddr::V6(String::from("::1"));
let m = Message::Write(String::from("hello"));
let m2 = Message::Write(String::from("hi"));
m.call();
m.res(&m2);
let some_number = Some(5);
let some_string = Some("a string");
let absend_number: Option<i32> = Option::None;
println!("{:?}", some_number);
println!("{:?}", some_string);
println!("{:?}", absend_number);
let x: i8 = 5;
let y: Option<i8> = Option::Some(5);
//let sum = x + y;
}
fn route(ip_type : IpAddrKind){}
impl Message{
fn call(&self){
println!("{:?}", self);
}
fn res(&self, other : &Message){
println!("m : [{:?}], m2 : [{:?}]", self, other);
}
}
참고 : rinthel.github.io/rust-lang-book-ko/ch06-01-defining-an-enum.html
'Rust-Language' 카테고리의 다른 글
if let 흐름 제어 (0) | 2021.04.13 |
---|---|
match 흐름 제어 연산자 (0) | 2021.04.09 |
method (0) | 2021.04.07 |
구조체 (0) | 2021.04.07 |
Slices (0) | 2021.04.05 |