Комбинаторы: and_then
map() описывался как использование цепочек
функций для упрощения выражения match.
Однако использование map() с функцией, которая в
качестве результата возвращает Option<T>
приводит к вложенности Option<Option<T>>. Такая цепочка из множества вызовов в итоге может
запутать. Вот тут и появляется другой комбинатор, зовущийся
and_then(), известный в некоторых языках как
flatmap.
and_then() запускает функцию, которая на вход получает обёрнутое значение, а возвращает результирующее
значение. Если Option равен None, то
он вернёт None.
В следующем примере, cookable_v2() возвращает
Option<Food>. Используя map()
вместо and_then() мы получим
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) => match have_ingredients(food) { None => None, Some(food) => Some(food), }, } } // Для удобства это может быть переписано с использованием более компактного `and_then()`: fn cookable_v2(food: Food) -> Option<Food> { have_recipe(food).and_then(have_ingredients) } fn eat(food: Food, day: Day) { match cookable_v2(food) { Some(food) => println!("Yay! В {:?} мы будем есть {:?}.", 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); }