Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

コンビネータ: map

matchOption を処理するための有効な方法です。しかし、いずれは 多用するのが煩わしいと感じるかもしれません。特に、入力がある場合にのみ有効な操作ではそうです。 このような場合、コンビネータを使用して、制御フローをモジュール的に 管理できます。

Option には map() という組み込みメソッドがあります。これは単純な Some -> SomeNone -> None のマッピングを行うコンビネータです。 複数の map() 呼び出しを連鎖させることで、さらに柔軟にできます。

次の例では、process() はコンパクトさを保ちながら、それ以前のすべての関数を 置き換えています。

#![allow(dead_code)]

#[derive(Debug)] enum Food { Apple, Carrot, Potato }

#[derive(Debug)] struct Peeled(Food);
#[derive(Debug)] struct Chopped(Food);
#[derive(Debug)] struct Cooked(Food);

// 食材の皮をむく。何もなければ、`None` を返す。
// それ以外の場合は、皮をむいた食材を返す。
fn peel(food: Option<Food>) -> Option<Peeled> {
    match food {
        Some(food) => Some(Peeled(food)),
        None       => None,
    }
}

// 食材を刻む。何もなければ、`None` を返す。
// それ以外の場合は、刻んだ食材を返す。
fn chop(peeled: Option<Peeled>) -> Option<Chopped> {
    match peeled {
        Some(Peeled(food)) => Some(Chopped(food)),
        None               => None,
    }
}

// 食材を調理する。ここでは、ケース処理に `match` の代わりに `map()` を使用する例を示す。
fn cook(chopped: Option<Chopped>) -> Option<Cooked> {
    chopped.map(|Chopped(food)| Cooked(food))
}

// 食材の皮をむき、刻み、調理する関数。すべてを順番に行う。
// コードを簡潔にするために、複数の `map()` の使用を連鎖させる。
fn process(food: Option<Food>) -> Option<Cooked> {
    food.map(|f| Peeled(f))
        .map(|Peeled(f)| Chopped(f))
        .map(|Chopped(f)| Cooked(f))
}

// 食べようとする前に、食べ物があるかどうか確認しよう!
fn eat(food: Option<Cooked>) {
    match food {
        Some(food) => println!("うーん。{:?} が大好きです", food),
        None       => println!("なんてこと!食べられませんでした。"),
    }
}

fn main() {
    let apple = Some(Food::Apple);
    let carrot = Some(Food::Carrot);
    let potato = None;

    let cooked_apple = cook(chop(peel(apple)));
    let cooked_carrot = cook(chop(peel(carrot)));
    // では、より簡潔に見える `process()` を試してみよう。
    let cooked_potato = process(potato);

    eat(cooked_apple);
    eat(cooked_carrot);
    eat(cooked_potato);
}

関連項目:

クロージャ, Option, Option::map()