コンビネータ: and_then
map() は、match 文を簡潔にするための連鎖可能な方法として説明しました。 しかし、Option<T> を返す関数に map() を使うと、ネストした Option<Option<T>> になります。複数の呼び出しを連鎖させると、 混乱しやすくなります。そこで、いくつかの言語では flatmap として知られる and_then() という別のコンビネータの出番です。
and_then() は、ラップされた値を使って入力された関数を呼び出し、その結果を返します。Option が None の場合は、代わりに None を返します。
次の例では、cookable_v3() は Option<Food> を返します。 and_then() の代わりに map() を使うと Option<Option<Food>> になってしまい、これは eat() に対して無効な型です。
#![allow(dead_code)] #[derive(Debug)] enum Food { CordonBleu, Steak, Sushi } #[derive(Debug)] enum Day { Monday, Tuesday, Wednesday } // Sushi を作るための材料はありません。 fn have_ingredients(food: Food) -> Option<Food> { match food { Food::Sushi => None, _ => Some(food), } } // Cordon Bleu 以外のすべてのレシピがあります。 fn have_recipe(food: Food) -> Option<Food> { match food { Food::CordonBleu => None, _ => Some(food), } } // 料理を作るには、レシピと材料の両方が必要です。 // このロジックは `match` の連鎖で表現できます: fn cookable_v1(food: Food) -> Option<Food> { match have_recipe(food) { None => None, Some(food) => have_ingredients(food), } } // これは `and_then()` を使うと、より簡潔に書き換えられます: fn cookable_v3(food: Food) -> Option<Food> { have_recipe(food).and_then(have_ingredients) } // そうしない場合、`Option<Option<Food>>` を `flatten()` して // `Option<Food>` を得る必要があります: fn cookable_v2(food: Food) -> Option<Food> { have_recipe(food).map(have_ingredients).flatten() } fn eat(food: Food, day: Day) { match cookable_v3(food) { Some(food) => println!("やった! {:?} には {:?} が食べられます。", day, food), None => println!("なんてこと。{:?} には食べられないのですか?", 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); }