Rust, 쉽게 하자!

Rust 입문

[Rust] Option

바로크냥 2022. 10. 7. 01:21

3. Option

option은 enum의 일종입니다. Rust에 미리 정의 되어 있습니다. 

Rust에서 null값의 가능성이 있는 상황을 처리하기 위해 아주 중요하게 사용 되는 특수 열거자라고 생각하면 됩니다. 

Rust의 중요한 특징 중에 하나가 null 값을 허용하지 않는다는 것입니다. 

대신 null 값이 발생할 수 있는 상황에서 Option을 사용 합니다.

다른 언어에서 null을 다루기 위한 타입을 보통 “Optioal”이라고 하는 것을 참고 하면 기억하기 쉽습니다.

우선 Option의 정의를 보겠습니다.

 

pub enum Option<T> {
    /// No value.
    #[lang = "None"]
    #[stable(feature = "Rust1", since = "1.0.0")]
    None,
    /// Some value of type `T`.
    #[lang = "Some"]
    #[stable(feature = "Rust1", since = "1.0.0")]
    Some(#[stable(feature = "Rust1", since = "1.0.0")] T),
}

복잡 합니다.

단순화하기 위해 여러 가지 수식어들을 지우고 뼈대만 남기고 보면 다음과 같습니다.

 

pub enum Option<T> {
    None,
    Some(T),
}

여기서 T는 제네릭이라고 하는데, 임의의 타입을 의미 합니다. 

None 에 있던 코멘트는 “// No value.”입니다.

즉 다른 언어의 null, None, nil에 해당 합니다. 

Some은 뭔가 값이 있다는 의미이고, Some(T)는 괄호 안의 T 타입 값을 가지고 있다는 의미입니다.

물론 이 값을 추출해서 사용할 수 있는 방법도 있습니다.

Option의 사용 예를 보겠습니다.

 

fn main() {
    let x:Option<i32> = None;      // 1
    match x {                      // 2
        None => {println!("No Value")}
        Some(_) => {println!("value exist")}
    }
}

// 출력
No Value

// 1: x 변수를 Option 타입으로 정의 했기 때문에 Option에 포함된 값 중에 하나인 None을 할당할 수 있습니다.

 

// 2: match 문을 사용해서 Option에 포함된 값인 None과 Some 각각의 경우에 실행할 코드를 만들어 주었습니다.

None이 아니라 Some 값을 갖는 경우를 예로 들어 보겠습니다.

 

fn main() {
    let x:Option<i32> = Some(30);   // 1
    match x {                        // 2
        None => {println!("No Value")}
        Some(v) => {println!("value exist : {}", v)}
    }
}

// 출력
value exist : 30

// 1: Option 타입으로 정의된 변수 x에 Some(30)을 할당했습니다.

 

// 2: match 문 안의 Some(v) 에서 v는 30을 가지고 있습니다. 

match 문을 사용하지 않고 Some이 가지고 있는 값을 사용하는 방법을 알아 보겠습니다.

먼저 변수 x를 포맷터를 사용 해서 출력 해 보겠습니다.

fn main() {
    let x:Option<i32> = Some(30);
    println!("Some has {:#?}", x);
}

// 출력
Some has Some(
    30,
)

포매터 ‘:#?’ 없이 실행해 보면 ‘:#?’을 사용 하라는 에러 메시지가 나옵니다.

이렇게 출력 하면 우리가 원하던 30이 출력 되지 않습니다. 

다음 코드는 30만 꺼내서 보여 줍니다.

fn main() {
    let x:Option<i32> = Some(30);
    println!("Some has {}", x.unwrap());      // 1
}

// 출력
Some has 30

// 1: x 대신 x.unwrap()을 사용하면 우리가 원하던 30이 출력 됩니다. 

x.unwrap()을 사용하면 ‘:#?’을 사용하지 않아도 됩니다.

왜냐하면 x.unwrap()은 x 변수의 값인 Some(30)에 저장 된 값인 30만 추출해서 반환하기 때문입니다.

30은 I32 타입이고 i32 타입은 println! 매크로에서 포맷터 없이 출력 가능 합니다.

만약 x.unwrap()을 None의 경우에 사용하면 어떻게 될까요?

 

fn main() {
    let x:Option<i32> = None;
    println!("Some has {}", x.unwrap());
}

// 출력
..에러
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value'
...

에러가 납니다.

그래서 unwrap 메소드는 값이 None 아닌것이 확실 때만 사용 해야 합니다. 

unwrap 메소드와 비슷한  expect 메소드도 있습니다.

에러일 때, 미리 지정한 메시지를 전달 합니다.

 

fn main() {
    let x: Option<i32> = Some(100);      // 1
    println!("Some has {}", x.expect("x fail!"));
    let y: Option<i32> = None;            // 2
    println!("Some has {}", y.expect("y fail!"));
}

// 출력
Some has 100
thread 'main' panicked at 'y fail!'
...

// 1: 값이 있을 때에는 x.expect 매소드를 사용 해도 unwrap 메소드를 사용 할 때처럼 값이 추출 되어서 출력이 됩니다. 

 

// 2: 값이 없을 때에는 메시지를 출력 하면서 panic을 발생 시킵니다.

panic은 Rust가 에러를 처리하는 방법 중에 하나 입니다.

panic은 원래의 위치로 돌아가지 않고 그대로 프로그램을 종료 시킵니다.

x.unwrap() 대신 x를 사용해 보겠습니다.

 

fn main() {
    let x:Option<i32> = None;
    println!("Some has {:#?}", x);
}

// 출력
Some has None

None이 출력 되었습니다.


정리 하겠습니다.

Option 열거자 타입은 어떤 값이 없을 수도 있는 상황에 사용 합니다.

match 문을 사용해서 안전하게 값을 가져 올 수 있습니다.

값이 None이 아닌 것이 확실 할 때, 단축형으로 간단하게 값을 사용 하려면 unwrap이나 expect 매소드를 사용할 수 있습니다.

 

 
반응형

'Rust 입문' 카테고리의 다른 글

[Rust] 소유권  (0) 2022.10.09
[Rust] Result와 Rust의 에러 처리  (0) 2022.10.07
[Rust] If let  (0) 2022.10.07
[Rust] match 변수 바인딩  (0) 2022.10.07
[Rust] Method  (2) 2022.10.07