enum Status {
ToDo,
InProgress,
Done,
}
위와 같은 Status enum은 C-style enum이라고 부른다.
각 variant는 named constant와 약간 유사한 간단한 label이다.
당신은 C, C++, Java, C#, Python 등과 같은 많은 programming languages에서 이러한 enum을 찾을 수 있다.
그러나 Rust enum은 무언가 더 할 수 있다.
각 variant에 data를 붙일 수 있다.
우리가 ticket작업을 하는 사람의 이름을 저장하길 원한다고 하자.
우리는 ticket이 in progress인 경우만 이 정보를 얻을 수 있다.
to-do ticket이나 done ticket일 경우는 정보를 얻을 수 없을 것이다.
우리는 InProgress variant에 String field를 붙임으로써, 이를 model할 수 있다.
enum Status {
ToDo,
InProgress {
assigned_to: String,
},
Done,
}
InProgress는 이제 struct-like variant다.
사실, struct를 정의할 때 사용했던 방식과 같은 문법을 쓴다.
enum안에 variant로서 inlined된 것 뿐이다.
만약 우리가 다음과 같이 Status instance의 assigned_to에 access하려 한다면
let status: Status = /* */;
// This won't compile
println!("Assigned to: {}", status.assigned_to);
compiler는 투정부린다.
error[E0609]: no field `assigned_to` on type `Status`
--> src/main.rs:5:40
|
5 | println!("Assigned to: {}", status.assigned_to);
| ^^^^^^^^^^^ unknown field
assigned_to는 variant-specific이다.
모든 Status instances에서 사용할 수 있는 것이 아니다.
assigned_to에 접근하기 위해서, 우리는 pattern matching을 사용할 필요가 있다.
match status {
Status::InProgress { assigned_to } => {
println!("Assigned to: {}", assigned_to);
},
Status::ToDo | Status::Done => {
println!("Done");
}
}
Status::InProgress { assigned_to } match pattern에서, assigned_to는 binding이다.
우리는 Status::InProgress variant를 destructuring하고,
new variable(assigned_to라는 이름의)에 assigned_to field를 binding한다.
만약 우리가 원한다면, 우리는 다른 variable name으로 field를 bind할 수 있다.
match status {
Status::InProgress { assigned_to: person } => {
println!("Assigned to: {}", person);
},
Status::ToDo | Status::Done => {
println!("Done");
}
}
03_variants_with_data exercise는 status의 variants data가 특정 조건일 때, statement를 수행하고
아닐시에는 전부 panic내버리는 것이었다.

String type을 &str로 변환할 때 &*(String name)이렇게 하면 된다.
그냥 str로 변환하려면 *(String name)이렇게 하면 되고...
이전 exercise의 답은 아마 이와 비슷할 것이다.
impl Ticket {
pub fn assigned_to(&self) -> &str {
match &self.status {
Status::InProgress { assigned_to } => assigned_to,
Status::Done | Status::ToDo => {
panic!("Only `In-Progress` tickets can be assigned to someone")
}
}
}
}
당신은 Status::InProgress variant에 대해서만 고려하면된다.
정말로 모든 variants를 match시켜야할까?
if let 구조는 enum의 다른 모든 variants를 다룰 필요없이
하나의 variant에 대해 match를 할 수 있게 해준다.
assigned_to method를 간결하게 하기 위해 if let을 다음과 같이 사용하면 된다.
impl Ticket {
pub fn assigned_to(&self) -> &str {
if let Status::InProgress { assigned_to } = &self.status {
assigned_to
} else {
panic!("Only `In-Progress` tickets can be assigned to someone");
}
}
}
else branch가 return early(panic과 같이)하다면,
let/else 구문을 사용할 수 있다.
impl Ticket {
pub fn assigned_to(&self) -> &str {
let Status::InProgress { assigned_to } = &self.status else {
panic!("Only `In-Progress` tickets can be assigned to someone");
};
assigned_to
}
}
어떤 right drift를 일으키지 않고 destructured variable을 할당할 수 있다.
즉, variable은 이전 code와 같은 level에서 할당된다.
그래서 assigned_to를 사용할 수 있다.
if let과 let/else는 관용적인 Rust 구문이다.
당신이 보기에 code의 readability를 향상시킬 수 있다면 사용하면되고,
아니라면 사용하지 않으면 된다.
04_if_let exercise는 if let을 사용하여, 하나의 경우에만 match하여 statement를 실행하고
나머지는 panic하는 것이었다.

assigned method에 대한 우리의 구현은 상당히 별로다.
to-do와 done에 대해 panicking하는 것은 이상과 멀다.
Rust의 option type을 사용해보자!
option은 nullable values를 나타내는 Rust type이다.
그것은 Rust의 standard library에 정의된 enum이다.
enum Option<T> {
Some(T),
None,
}
option은 value가 present(Some(T))하거나 absent(None)할 수 있다는 생각을 encode한다.
또한 두 cases모두 explicitly하게 다뤄야한다.
만약 당신이 nullable value를 다루는데 , None case를 다루는 것을 까먹었다면,
compile error가 뜰 것이다.
이는 null check를 잊어버려 runtime error가 발생할 수 있는
다른 언어의 implicitly nullability에 비해 많이 발전한 형태다.
option의 정의는 이전에 보지못한 Rust 구조를 사용한다.
그것은 tuple-like variants다.
option은 Some(T)와 None 두 variants를 가진다.
Some은 unnamed fields를 가진 variant를 일컫는 tuple-like variant다.
Tuple-like variants는 하나의 저장할 field가 있을 때,
특히 option과 같은 wrapper type을 볼 때, 자주 사용한다.
이는 enums에만 국한되지 않는다.
tuple-like structs도 정의할 수 있다.
struct Point(i32, i32);
positional index를 사용해 Point instance의 두 개의 fields에 접근할 수 있다.
let point = Point(3, 4);
let x = point.0;
let y = point.1;
아직 tuples에 대해 보지 않았기 때문에, tuple-like라는 것이 기이할 수 있다.
Tuples는 primitive Rust type 중 하나다.
types를 사용하여 고정된 수의 values를 그룹화한다.
// Two values, same type
let first: (i32, i32) = (3, 4);
// Three values, different types
let second: (i32, u32, u8) = (-42, 3, 8);
문법은 간단하다.
당신은 values의 types를 괄호안에 comma로 구분해 list하면된다.
당신은 dot notation과 field index를 통해 tuple의 fields에 접근할 수 있다.
assert_eq!(second.0, -42);
assert_eq!(second.1, 3);
assert_eq!(second.2, 8);
Tuples는 어떤것 전용 struct type을 정의할 필요가 없을 때,
values를 그룹화하는 편리한 방법이다.
05_nullability exercise는 if let else 구문과 Option을 사용하여 구현했다.

compiler가 하라는대로 하니까 된다.
Rust에는 기능이 참 많은 것 같다.
그런데 전부 compiler와 관련된 기능이며,
run time에 시스템적으로 error날 일은 거의 없다고 볼 수 있을 것 같다.
'Rust Programming' 카테고리의 다른 글
| [Rust] 5. Ticket v2: Packages, Dependencies, thiserror (0) | 2024.08.05 |
|---|---|
| [Rust] 5. Ticket v2: Fallibility, Unwrap, Error enums, Error trait (0) | 2024.08.02 |
| [Rust] 5. Ticket v2: Enums, Branching(match) (0) | 2024.07.31 |
| [Rust] 4. Traits: Drop trait (0) | 2024.07.31 |
| [Rust] 4. Traits: Associated and generic types, Clone trait, Copy trait (0) | 2024.07.30 |