Option 풀기와 기본값
Option이 None일 때 이를 풀고 기본값으로 돌아가는 방법은 여러 가지가 있습니다. 우리의 필요에 맞는 것을 선택하려면 다음을 고려해야 합니다:
- 조급한(eager) 평가가 필요한가요, 아니면 느긋한(lazy) 평가가 필요한가요?
- 원래의 빈 값을 그대로 두어야 하나요, 아니면 그 자리에서 수정해야 하나요?
or()은 체인 가능하며, 조급하게 평가하고, 빈 값을 그대로 유지합니다.
or()은 체인 가능하며 다음 예제에서 보듯 인자를 조급하게 평가합니다. or의 인자들은 조급하게 평가되기 때문에, or에 전달된 변수는 이동(move)된다는 점에 유의하세요.
#[derive(Debug)]
enum Fruit { Apple, Orange, Banana, Kiwi, Lemon }
fn main() {
let apple = Some(Fruit::Apple);
let orange = Some(Fruit::Orange);
let no_fruit: Option<Fruit> = None;
let first_available_fruit = no_fruit.or(orange).or(apple);
println!("first_available_fruit: {:?}", first_available_fruit);
// first_available_fruit: Some(Orange)
// `or`는 인자를 이동시킵니다.
// 위 예제에서 `or(orange)`가 `Some`을 반환했으므로 `or(apple)`은 호출되지 않았습니다.
// 하지만 `apple`이라는 이름의 변수는 상관없이 이동되었으며, 더 이상 사용할 수 없습니다.
// println!("Variable apple was moved, so this line won't compile: {:?}", apple);
// TODO: 위 줄의 주석을 해제하여 컴파일러 에러를 확인해 보세요.
}
or_else()는 체인 가능하며, 느긋하게 평가하고, 빈 값을 그대로 유지합니다.
또 다른 대안은 or_else를 사용하는 것입니다. 이 역시 체인 가능하며 다음 예제에서 보듯 느긋하게 평가합니다:
#[derive(Debug)]
enum Fruit { Apple, Orange, Banana, Kiwi, Lemon }
fn main() {
let no_fruit: Option<Fruit> = None;
let get_kiwi_as_fallback = || {
println!("키위를 대체값으로 제공함");
Some(Fruit::Kiwi)
};
let get_lemon_as_fallback = || {
println!("레몬을 대체값으로 제공함");
Some(Fruit::Lemon)
};
let first_available_fruit = no_fruit
.or_else(get_kiwi_as_fallback)
.or_else(get_lemon_as_fallback);
println!("first_available_fruit: {:?}", first_available_fruit);
// 키위를 대체값으로 제공함
// first_available_fruit: Some(Kiwi)
}
get_or_insert()는 조급하게 평가하며, 빈 값을 그 자리에서 수정합니다.
Option에 값이 포함되도록 보장하기 위해, 다음 예제에서 보듯 get_or_insert를 사용하여 대체값으로 그 자리에서 수정할 수 있습니다. get_or_insert는 파라미터를 조급하게 평가하므로, apple 변수는 이동된다는 점에 유의하세요.
#[derive(Debug)]
enum Fruit { Apple, Orange, Banana, Kiwi, Lemon }
fn main() {
let mut my_fruit: Option<Fruit> = None;
let apple = Fruit::Apple;
let first_available_fruit = my_fruit.get_or_insert(apple);
println!("first_available_fruit is: {:?}", first_available_fruit);
println!("my_fruit is: {:?}", my_fruit);
// first_available_fruit is: Apple
// my_fruit is: Some(Apple)
//println!("apple이라는 이름의 변수는 이동되었습니다: {:?}", apple);
// TODO: 위 줄의 주석을 해제하여 컴파일러 에러를 확인해 보세요.
}
get_or_insert_with()는 느긋하게 평가하며, 빈 값을 그 자리에서 수정합니다.
대체할 값을 명시적으로 제공하는 대신, 다음과 같이 get_or_insert_with에 클로저를 전달할 수 있습니다:
#[derive(Debug)]
enum Fruit { Apple, Orange, Banana, Kiwi, Lemon }
fn main() {
let mut my_fruit: Option<Fruit> = None;
let get_lemon_as_fallback = || {
println!("레몬을 대체값으로 제공함");
Fruit::Lemon
};
let first_available_fruit = my_fruit
.get_or_insert_with(get_lemon_as_fallback);
println!("first_available_fruit is: {:?}", first_available_fruit);
println!("my_fruit is: {:?}", my_fruit);
// 레몬을 대체값으로 제공함
// first_available_fruit is: Lemon
// my_fruit is: Some(Lemon)
// Option에 값이 있으면 변경되지 않은 채로 유지되며, 클로저는 호출되지 않습니다.
let mut my_apple = Some(Fruit::Apple);
let should_be_apple = my_apple.get_or_insert_with(get_lemon_as_fallback);
println!("should_be_apple is: {:?}", should_be_apple);
println!("my_apple is unchanged: {:?}", my_apple);
// 출력은 다음과 같습니다. `get_lemon_as_fallback` 클로저가 호출되지 않았음에 유의하세요.
// should_be_apple is: Apple
// my_apple is unchanged: Some(Apple)
}
참고:
클로저(closures), get_or_insert, get_or_insert_with, 이동된 변수(moved variables), or, or_else