コンビネータ: map
match は Option を処理するための有効な方法です。しかし、いずれは 多用するのが煩わしいと感じるかもしれません。特に、入力がある場合にのみ有効な操作ではそうです。 このような場合、コンビネータを使用して、制御フローをモジュール的に 管理できます。
Option には map() という組み込みメソッドがあります。これは単純な Some -> Some と None -> 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); }