Post

10. Rust 데이터 유형에 강력한 성능을 제공하는 Enum

Rust의 열거형(enums)에 대해 이야기해보려고 합니다. Rust에서의 열거형은 C 언어의 열거형과는 다르게 하스켈(Haskell)의 대수적 데이터 타입과 더 비슷합니다.

열거형을 정의할 때는 enum 키워드를 사용하며, 대문자로 카멜케이스로 된 이름과 해당하는 변형들을 중괄호 안에 나열합니다.

Defining Enum

이렇게 정의한 열거형을 단순히 사용할 수도 있습니다. 이 경우 C 언어의 열거형과 비슷한 방식으로 사용할 수 있습니다. 열거형에 데이터와 메서드를 연결하는 것이 Rust 열거형의 진정한 힘입니다.

Basic Enum Usage

아래의 DispenserItem 예시를 살펴봅시다.

1
2
3
4
5
6
enum DispenserItem {
    Empty,
    Ammo(u8),
    Things(String, i32),
    Place { x: i32, y: i32 },
}

여기서 DispenserItemEmpty처럼 데이터가 없는 이름있는 변형, Ammo처럼 단일 데이터, Things처럼 데이터가 튜플이나 익명 구조체로 묶인 변형 등 여러 가지 형태가 될 수 있습니다.

이렇게 선언한 값들은 다음과 같이 사용 가능합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
enum DispenserItem {
    Empty,
    Ammo(u8),
    Things(String, i32),
    Place { x: i32, y: i32 },
}

fn main() {
    // DispenserItem 열거형 사용 예제
    let item1 = DispenserItem::Empty;
    let item2 = DispenserItem::Ammo(10);
    let item3 = DispenserItem::Things(String::from("Gadget"), 42);
    let item4 = DispenserItem::Place { x: 10, y: 20 };

    // 각각의 항목 출력
    match item1 {
        DispenserItem::Empty => println!("Item is Empty"),
        DispenserItem::Ammo(amount) => println!("Item is Ammo with amount: {}", amount),
        DispenserItem::Things(name, value) => println!("Item is Things: {} - {}", name, value),
        DispenserItem::Place { x, y } => println!("Item is Place at ({}, {})", x, y),
    }
}

Implementing Functions and Methods

또한, 열거형에 함수와 메서드를 구현할 수 있습니다. 열거형은 제네릭과 함께 사용될 수도 있습니다. 표준 라이브러리에서 자주 사용되는 Option 열거형을 예로 들어 보겠습니다.

Using Enums with Generics: Option

Option 열거형은 값이 존재할 수도, 존재하지 않을 수도 있는 경우에 사용됩니다.

1
2
3
4
enum Option<T> {
    Some(T),
    None,
}

여기서 T는 임의의 타입을 나타냅니다. 이러한 제네릭 열거형은 많은 경우에서 유용하게 사용됩니다. Option은 값을 갖는 Some 변형과 값을 갖지 않는 None 변형으로 구성됩니다.

Some enumif let Some(xx) 구문과 match를 이용해 사용하는 예제 코드입니다.

다양한 방법으로 Some enum 에서 값을 가지고 올 수 있는 것 을 알 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
use std::collections::HashMap;

fn main() {
    let mut x = None;
    x = Some(5);
    if let Some(b) = x {
        println!("1. value is {}", b);
    }
    
    x.is_some(); // true
    x.is_none(); // false

    match x {
        Some(value) => {
            println!("2. {}", value)
        },
        None => {
            println!("2. None value")
        }
    }

    let x2 = match x {
        Some(value) => {
            value + 1
        },
        None => {
            0
        }
    };

    println!("3. {}", x2);
}

Handling Enum with Patterns

열거형은 다양한 데이터를 나타낼 수 있기 때문에 패턴을 사용하여 이를 처리해야 합니다. 단일 변형을 확인하려면 “if let” 표현식을 사용합니다.

1
2
3
4
5
if let Some(value) = some_option {
    println!("Value: {}", value);
} else {
    println!("Option is None");
}

모든 변형을 처리해야 하는 경우에는 match 표현식을 사용합니다.

1
2
3
4
match result {
    Ok(value) => println!("Value: {}", value),
    Err(error) => println!("Error: {}", error),
}

Special Enums in Standard Library

표준 라이브러리에서 자주 사용되는 두 가지 특별한 열거형에 대해 조금 더 자세히 살펴보겠습니다.

Option: Handling Absence

Option은 값의 존재 여부를 나타냅니다. SomeNone은 어떤 값이 있는지 없는지를 나타냅니다.

1
2
3
4
5
let some_value: Option<i32> = Some(5);
let none_value: Option<i32> = None;

println!("Is some_value Some? {}", some_value.is_some()); // true
println!("Is none_value None? {}", none_value.is_none()); // true

Result: Dealing with Errors

Result는 작업의 결과 또는 오류를 나타냅니다. 주로 IO 모듈에서 자주 사용됩니다.

1
2
3
4
5
6
7
8
use std::fs::File;

let file_result = File::open("example.txt");

match file_result {
    Ok(file) => println!("File opened successfully: {:?}", file),
    Err(error) => println!("Error opening file: {}", error),
}

이렇게 함으로써, 열거형을 사용하여 다양한 상황을 다룰 수 있습니다. Rust에서 열거형은 데이터를 구조화하고 처리하는 강력한 도구입니다

This post is licensed under CC BY 4.0 by the author.