16.2.2 Combinators: and_then

map()match를 단순화하여 연계할 수 있는 문법이다. 하지만, map()Option<T>를 반환하는 함수에 사용하면 중첩된 Option<Option<T>>를 결과물로 낸다. 여러 호출을 연속으로 함께 사용하는 것은 혼란스럽게 만든다. 그럴 때 쓰라고 다른 연결자 and_then()이 있고, 어떤 언어에서는 플랫맵(flatmap)이라고 알려져 있다.

and_then() 호출은 그 자신의 함수에 포장된 값을 넣고 결과를 반환한다. 만약 OptionNone이면, 그땐 None을 대신 반환한다.

다음의 예제에서 cookable_v2()Option<Food> 결과물로 낸다. map()and_then()대신 사용하면 Option<Option<Food>>를 반환하게 되고, 이는 eat()을 호출할 적합한 타입이 아니다. In the following example, cookable_v2() results in an Option<Food>.

#![allow(dead_code)]

#[derive(Debug)] enum Food { CordonBleu, Steak, Sushi }
#[derive(Debug)] enum Day { Monday, Tuesday, Wednesday }

// 초밥을 만들 재료가 없다.
fn have_ingredients(food: Food) -> Option<Food> {
    match food {
        Food::Sushi => None,
        _           => Some(food),
    }
}

// 치즈돈까스를 제외한 모든 것에 대한 제조법이 있다.
fn have_recipe(food: Food) -> Option<Food> {
    match food {
        Food::CordonBleu => None,
        _                => Some(food),
    }
}

// 요리를 만들기 위해서는 재료와 조리법이 모두 필요하다.
// 우리는 `match`들의 체인으로 이를 논리적으로 표현할 수 있다.
fn cookable_v1(food: Food) -> Option<Food> {
    match have_ingredients(food) {
        None       => None,
        Some(food) => match have_recipe(food) {
            None       => None,
            Some(food) => Some(food),
        },
    }
}

// 이것은 `and_then()`을 사용하면 더 간단하게 재작성 될 수 있다.
fn cookable_v2(food: Food) -> Option<Food> {
    have_ingredients(food).and_then(have_recipe)
}

fn eat(food: Food, day: Day) {
    match cookable_v2(food) {
        Some(food) => println!("Yay! On {:?} we get to eat {:?}.", day, food),
        None       => println!("Oh no. We don't get to eat on {:?}?", day),
    }
}

fn main() {
    let (cordon_bleu, steak, sushi) = (Food::CordonBleu, Food::Steak, Food::Sushi);

    eat(cordon_bleu, Day::Monday);
    eat(steak, Day::Tuesday);
    eat(sushi, Day::Wednesday);
}

See also:

closures, Option, and Option::and_then()

results matching ""

    No results matching ""