コンビネータ：map - Rust By Example
Description: Rust by Example (RBE) is a collection of runnable examples that illustrate various Rust concepts and standard libraries.

## 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`

`match`は`Option`を扱うのに適したメソッドです。しかし、大量にこれを使用しているとじきに億劫になってくるでしょう。引数の値が有効である（訳注: この場合は`None`でない）必要がある関数を扱う際には特にそうです。そうした場合には、コンビネータを使うと、処理の流れをモジュール化されたやり方で管理できます。

`Some -> Some`あるいは`None -> None`の単純な操作を適用する必要がある場合には、`Option`は`map()`というビルトインのメソッドを提供していますので、これを使用しましょう。`map()`のフレキシビリティは、複数の`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,
        }
    }

    // 上と同じように、食べ物を切る前に、皮を向いた食べ物の有無を知る必要があります。
    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!("Mmm. I love {:?}", food),
            None       => println!("Oh no! It wasn't edible."),
        }
    }

    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()`