Option 열거형

다음과 같은 함수를 생각해 보겠습니다. 입력받는 파라미터에 따라서 결과값이 있을 수도 있고, 없을 수도 있습니다.

#![allow(unused)]
fn main() {
fn give_some_or_none(some: bool) -> Option<String> {
    if some {
        Some(String::from("💖"))
    } else {
        None
    }
}
}

이전에 열거형에서 배웠던 것처럼, 어떤 계산의 결과가 비어 있거나 없을 가능성이 있는 경우, std 라이브러리의 Option<T>을 써서 이를 표현할 수 있습니다. 문제는 이를 사용하는 곳에서 결과값을 어떻게 처리할 것인지입니다. 앞에서는 match문을 사용해 각 경우를 모두 처리할 수 있다고 배웠습니다. 이렇게 각 경우를 명시적으로 처리할 수도 있지만, 암묵적으로 함수의 결과를 처리하는 방법인 unwrap 을 사용할 수도 있습니다.

fn give_some_or_none(some: bool) -> Option<String> {
    if some {
        Some(String::from("💖"))
    } else {
        None
    }
}

fn main() {
    println!("{}", give_some_or_none(true).unwrap());
}

그런데 만일 give_some_or_none 함수에 false가 주어진다면 어떨까요?

fn main() {
    println!("{}", give_some_or_none(false).unwrap());
}

실행 결과

thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', src/main.rs:10:45

여기서 알 수 있듯이, unwrap을 사용하면 None이 리턴되는 경우에 패닉이 발생합니다. 따라서 Option<T>를 리턴하는 함수에 unwrap을 사용하려면 해당 함수가 반드시 Some을 리턴하는 것이 확실해야만 합니다. 그렇지 않으면 match를 사용해 모든 경우를 처리하는 게 올바른 방법입니다.

unwrap_or...

하지만 때로는 Some인 경우는 그냥 값을 바로 사용하고, 에러가 발생하는 경우만 따로 처리하고 싶은 경우가 있습니다. if let Some 을 사용해도 되지만, 함수형 프로그래밍답게 처리하는 세 가지 방법이 존재합니다.

unwrap_or

포함된 Some 값 또는 제공된 기본값을 반환합니다.

unwrap_or에 전달된 인수는 즉시 평가됩니다. 함수 호출의 결과를 전달하는 경우 느리게 평가되는 unwrap_or_else를 사용하는 것이 좋습니다.

fn main() {
    assert_eq!(Some("car").unwrap_or("bike"), "car");
    assert_eq!(None.unwrap_or("bike"), "bike");
}

unwrap_or_else

포함된 Some 값을 반환하거나 클로저에서 계산합니다.

#![allow(unused)]
fn main() {
let k = 10;
assert_eq!(Some(4).unwrap_or_else(|| 2 * k), 4);
assert_eq!(None.unwrap_or_else(|| 2 * k), 20);
}

unwrap_or_default

포함된 Some 값 또는 기본값을 반환합니다.

self 인자를 소비한 다음 Some이면 포함된 값을 반환하고, None이면 해당 타입의 기본값을 반환합니다.

#![allow(unused)]
fn main() {
let x: Option<u32> = None;
let y: Option<u32> = Some(12);

assert_eq!(x.unwrap_or_default(), 0);
assert_eq!(y.unwrap_or_default(), 12);
}

?

결과가 Some 이 아닌 경우 즉시 함수를 종료하고 None을 반환합니다.

fn give_some_or_none(some: bool) -> Option<String> {
    if some {
        Some(String::from("💖"))
    } else {
        None
    }
}

fn question_mark(bool: bool) -> Option<String> {
    let some = give_some_or_none(bool)?;
    Some(some)
}

fn main() {
    println!("{:?}", question_mark(true));
    println!("{:?}", question_mark(false));
}