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)); }