コンビネータ:map
match
is a valid method for handling Option
s. However, you may eventually find heavy usage tedious, especially with operations only valid with an input. In these cases, combinators can be used to manage control flow in a modular fashion.
Option
has a built in method called map()
, a combinator for the simple mapping of Some -> Some
and None -> None
. Multiple map()
calls can be chained together for even more flexibility.
以下の例では、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); }